feat: add cursor change

This commit is contained in:
NBonaparte 2017-09-02 21:45:45 -07:00
parent a682d2af91
commit 65edba3321
10 changed files with 150 additions and 4 deletions

View File

@ -9,6 +9,7 @@ checklib(ENABLE_MPD "pkg-config" libmpdclient)
checklib(ENABLE_NETWORK "cmake" Libiw) checklib(ENABLE_NETWORK "cmake" Libiw)
checklib(WITH_XRM "pkg-config" xcb-xrm) checklib(WITH_XRM "pkg-config" xcb-xrm)
checklib(WITH_XRANDR_MONITORS "pkg-config" "xcb-randr>=1.12") 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) if(NOT DEFINED ENABLE_CCACHE AND CMAKE_BUILD_TYPE_UPPER MATCHES DEBUG)
set(ENABLE_CCACHE ON) set(ENABLE_CCACHE ON)
@ -36,6 +37,7 @@ option(WITH_XSYNC "xcb-sync support" OFF)
option(WITH_XCOMPOSITE "xcb-composite support" OFF) option(WITH_XCOMPOSITE "xcb-composite support" OFF)
option(WITH_XKB "xcb-xkb support" ON) option(WITH_XKB "xcb-xkb support" ON)
option(WITH_XRM "xcb-xrm support" ON) option(WITH_XRM "xcb-xrm support" ON)
option(WITH_CURSOR "xcb-cursor support" ON)
if(CMAKE_BUILD_TYPE_UPPER MATCHES DEBUG) if(CMAKE_BUILD_TYPE_UPPER MATCHES DEBUG)
option(DEBUG_LOGGER "Debug logging" ON) option(DEBUG_LOGGER "Debug logging" ON)

View File

@ -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_XRENDER "pkg-config" xcb-render libs dirs)
querylib(WITH_XRM "pkg-config" xcb-xrm libs dirs) querylib(WITH_XRM "pkg-config" xcb-xrm libs dirs)
querylib(WITH_XSYNC "pkg-config" xcb-sync libs dirs) querylib(WITH_XSYNC "pkg-config" xcb-sync libs dirs)
querylib(WITH_CURSOR "pkg-config" xcb-cursor libs dirs)

View File

@ -28,6 +28,7 @@ colored_option(" xcb-sync" WITH_XSYNC)
colored_option(" xcb-composite" WITH_XCOMPOSITE) colored_option(" xcb-composite" WITH_XCOMPOSITE)
colored_option(" xcb-xkb" WITH_XKB) colored_option(" xcb-xkb" WITH_XKB)
colored_option(" xcb-xrm" WITH_XRM) colored_option(" xcb-xrm" WITH_XRM)
colored_option(" xcb-cursor" WITH_CURSOR)
if(CMAKE_BUILD_TYPE_UPPER MATCHES DEBUG) if(CMAKE_BUILD_TYPE_UPPER MATCHES DEBUG)
message(STATUS " Debug options:") message(STATUS " Debug options:")

View File

@ -26,9 +26,9 @@ class tray_manager;
// }}} // }}}
class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::property_notify, evt::enter_notify, class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::property_notify, evt::enter_notify,
evt::leave_notify, evt::destroy_notify, evt::client_message>, evt::leave_notify, evt::motion_notify, evt::destroy_notify, evt::client_message>,
public signal_receiver<SIGN_PRIORITY_BAR, signals::eventqueue::start, signals::ui::tick, public signal_receiver<SIGN_PRIORITY_BAR, signals::eventqueue::start, signals::ui::tick,
signals::ui::shade_window, signals::ui::unshade_window, signals::ui::dim_window> { signals::ui::shade_window, signals::ui::unshade_window, signals::ui::dim_window, signals::ui::cursor_change> {
public: public:
using make_type = unique_ptr<bar>; using make_type = unique_ptr<bar>;
static make_type make(bool only_initialize_values = false); static make_type make(bool only_initialize_values = false);
@ -56,6 +56,7 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
void handle(const evt::destroy_notify& evt); void handle(const evt::destroy_notify& evt);
void handle(const evt::enter_notify& evt); void handle(const evt::enter_notify& evt);
void handle(const evt::leave_notify& evt); void handle(const evt::leave_notify& evt);
void handle(const evt::motion_notify& evt);
void handle(const evt::button_press& evt); void handle(const evt::button_press& evt);
void handle(const evt::expose& evt); void handle(const evt::expose& evt);
void handle(const evt::property_notify& evt); void handle(const evt::property_notify& evt);
@ -65,6 +66,7 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
bool on(const signals::ui::shade_window&); bool on(const signals::ui::shade_window&);
bool on(const signals::ui::tick&); bool on(const signals::ui::tick&);
bool on(const signals::ui::dim_window&); bool on(const signals::ui::dim_window&);
bool on(const signals::ui::cursor_change&);
private: private:
connection& m_connection; connection& m_connection;
@ -85,6 +87,7 @@ class bar : public xpp::event::sink<evt::button_press, evt::expose, evt::propert
mousebtn m_buttonpress_btn{mousebtn::NONE}; mousebtn m_buttonpress_btn{mousebtn::NONE};
int m_buttonpress_pos{0}; int m_buttonpress_pos{0};
int m_motion_pos{0};
event_timer m_buttonpress{0L, 5L}; event_timer m_buttonpress{0L, 5L};
event_timer m_doubleclick{0L, 150L}; event_timer m_doubleclick{0L, 150L};

View File

@ -153,6 +153,10 @@ struct bar_settings {
bool override_redirect{false}; bool override_redirect{false};
string cursor{};
string cursor_click{};
string cursor_scroll{};
vector<action> actions{}; vector<action> actions{};
bool dimmed{false}; bool dimmed{false};

View File

@ -102,6 +102,9 @@ namespace signals {
struct button_press : public detail::value_signal<button_press, string> { struct button_press : public detail::value_signal<button_press, string> {
using base_type::base_type; using base_type::base_type;
}; };
struct cursor_change : public detail::value_signal<cursor_change, string> {
using base_type::base_type;
};
struct visibility_change : public detail::value_signal<visibility_change, bool> { struct visibility_change : public detail::value_signal<visibility_change, bool> {
using base_type::base_type; using base_type::base_type;
}; };

View File

@ -32,6 +32,7 @@ namespace signals {
struct changed; struct changed;
struct tick; struct tick;
struct button_press; struct button_press;
struct cursor_change;
struct visibility_change; struct visibility_change;
struct dim_window; struct dim_window;
struct shade_window; struct shade_window;

22
include/x11/cursor.hpp Normal file
View File

@ -0,0 +1,22 @@
#pragma once
//#if not WITH_CURSOR
//#error "Not built with support for xcb-cursor..."
//#endif
#include <xcb/xcb_cursor.h>
#include "common.hpp"
#include "x11/connection.hpp"
#include "utils/string.hpp"
POLYBAR_NS
namespace cursor_util {
static const vector<string> pointer_names {"pointing_hand", "pointer", "hand", "hand1", "hand2", "e29285e634086352946a0e7090d73106", "9d800788f1b08800ae810202380a0822"};
static const vector<string> arrow_names {"left_ptr", "arrow", "dnd-none", "op_left_arrow"};
static const vector<string> 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

View File

@ -16,6 +16,7 @@
#include "utils/string.hpp" #include "utils/string.hpp"
#include "x11/atoms.hpp" #include "x11/atoms.hpp"
#include "x11/connection.hpp" #include "x11/connection.hpp"
#include "x11/cursor.hpp"
#include "x11/ewmh.hpp" #include "x11/ewmh.hpp"
#include "x11/extensions/all.hpp" #include "x11/extensions/all.hpp"
#include "x11/icccm.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 = m_conf.get(bs, "dim-value", 1.0);
m_opts.dimvalue = math_util::cap(m_opts.dimvalue, 0.0, 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 // Build WM_NAME
m_opts.wmname = m_conf.get(bs, "wm-name", "polybar-" + bs.substr(4) + "_" + m_opts.monitor->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, " ", "-"); m_opts.wmname = string_util::replace(m_opts.wmname, " ", "-");
@ -554,7 +557,6 @@ void bar::handle(const evt::enter_notify&) {
} }
#endif #endif
#endif #endif
if (m_opts.dimmed) { if (m_opts.dimmed) {
m_taskqueue->defer_unique("window-dim", 25ms, [&](size_t) { m_taskqueue->defer_unique("window-dim", 25ms, [&](size_t) {
m_opts.dimmed = false; m_opts.dimmed = false;
@ -580,7 +582,6 @@ void bar::handle(const evt::leave_notify&) {
} }
#endif #endif
#endif #endif
if (!m_opts.dimmed) { if (!m_opts.dimmed) {
m_taskqueue->defer_unique("window-dim", 3s, [&](size_t) { m_taskqueue->defer_unique("window-dim", 3s, [&](size_t) {
m_opts.dimmed = true; 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 * Event handler for XCB_BUTTON_PRESS events
* *
@ -703,6 +768,9 @@ bool bar::on(const signals::eventqueue::start&) {
if (m_opts.dimvalue != 1.0) { if (m_opts.dimvalue != 1.0) {
m_connection.ensure_event_mask(m_opts.window, XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW); 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)); m_log.info("Bar window: %s", m_connection.id(m_opts.window));
restack_window(); restack_window();
@ -841,4 +909,12 @@ bool bar::on(const signals::ui::dim_window& sig) {
return false; 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 POLYBAR_NS_END

33
src/x11/cursor.cpp Normal file
View File

@ -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<string> *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