-
-
Notifications
You must be signed in to change notification settings - Fork 691
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Change cursors over clickable/scrollable areas #727
Changes from all commits
65edba3
1dc111c
014a9f4
1886cf9
b7f1960
e612fe1
81c83c4
70023b0
60f8451
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#pragma once | ||
|
||
#include "settings.hpp" | ||
|
||
#if not WITH_XCURSOR | ||
#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 map<string, vector<string>> cursors = { | ||
{"pointer", {"pointing_hand", "pointer", "hand", "hand1", "hand2", "e29285e634086352946a0e7090d73106", "9d800788f1b08800ae810202380a0822"}}, | ||
{"default", {"left_ptr", "arrow", "dnd-none", "op_left_arrow"}}, | ||
{"ns-resize", {"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 valid(string name); | ||
bool set_cursor(xcb_connection_t *c, xcb_screen_t *screen, xcb_window_t w, string name); | ||
} | ||
|
||
POLYBAR_NS_END |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,10 @@ | |
#include "x11/icccm.hpp" | ||
#include "x11/tray_manager.hpp" | ||
|
||
#if WITH_XCURSOR | ||
#include "x11/cursor.hpp" | ||
#endif | ||
|
||
#if ENABLE_I3 | ||
#include "utils/i3.hpp" | ||
#endif | ||
|
@@ -126,6 +130,19 @@ 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think having There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also it would be useful to the user if polybar showed a warning, if the config uses an invalid cursor name. |
||
#if WITH_XCURSOR | ||
if (!m_opts.cursor_click.empty() && !cursor_util::valid(m_opts.cursor_click)) { | ||
m_log.warn("Ignoring unsupported cursor-click option '%s'", m_opts.cursor_click); | ||
m_opts.cursor_click.clear(); | ||
} | ||
if (!m_opts.cursor_scroll.empty() && !cursor_util::valid(m_opts.cursor_scroll)) { | ||
m_log.warn("Ignoring unsupported cursor-scroll option '%s'", m_opts.cursor_scroll); | ||
m_opts.cursor_scroll.clear(); | ||
} | ||
#endif | ||
|
||
// 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 +571,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 +596,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 +604,69 @@ 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); | ||
#if WITH_XCURSOR | ||
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 = [&](const action& action) { | ||
if (!m_opts.cursor_click.empty() && !(action.button == mousebtn::SCROLL_UP || action.button == mousebtn::SCROLL_DOWN || action.button == mousebtn::NONE)) { | ||
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) { | ||
found_scroll = 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) { | ||
if (!string_util::compare(m_opts.cursor, m_opts.cursor_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) { | ||
if (!string_util::compare(m_opts.cursor, m_opts.cursor_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; | ||
} | ||
#endif | ||
} | ||
|
||
/** | ||
* Event handler for XCB_BUTTON_PRESS events | ||
* | ||
|
@@ -703,6 +781,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 +922,14 @@ bool bar::on(const signals::ui::dim_window& sig) { | |
return false; | ||
} | ||
|
||
#if WITH_XCURSOR | ||
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; | ||
} | ||
#endif | ||
|
||
POLYBAR_NS_END |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#include "x11/cursor.hpp" | ||
|
||
POLYBAR_NS | ||
|
||
namespace cursor_util { | ||
bool valid(string name) { | ||
if (cursors.find(name) != cursors.end()) | ||
return true; | ||
return false; | ||
} | ||
|
||
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; | ||
} | ||
for (auto&& cursor_name : cursors.at(name)) { | ||
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do I understand this right that there are three types of cursors
pointer
,ns-resize
anddefault
and these vectors are just a list of fallback names if the xcb doesn't recognize a certain cursor name?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I have chosen three relevant cursors to be used (we could add more but IMO those are the ones which are most likely to be used). Their names are not standardized so we have to cycle through them until we find one that exists in the current theme.