mirror of
https://github.com/Llewellynvdm/conky.git
synced 2024-11-17 02:25:09 +00:00
Fix event propagation on Openbox
Events now get correctly propagated to a window (or root if none) behind conky. This was a necessary change to handle cases such as MATE+caja where caja is used between conky and background to show icons and desktop menu. * Don't change input focus on propagation Could cause input focus flickering, so I'm leaving it up to WMs to manage focus. Signed-off-by: Tin Švagelj <tin.svagelj@live.com>
This commit is contained in:
parent
0419e01fe4
commit
9424ab8ca5
@ -410,6 +410,8 @@ bool display_output_x11::main_loop_wait(double t) {
|
||||
if (data->evtype == XI_Motion && is_cursor_move) {
|
||||
Window query_result =
|
||||
query_x11_window_at_pos(display, data->root_x, data->root_y);
|
||||
// query_result is not window.window in some cases.
|
||||
query_result = query_x11_last_descendant(display, query_result);
|
||||
|
||||
static bool cursor_inside = false;
|
||||
|
||||
|
193
src/x11.cc
193
src/x11.cc
@ -1352,43 +1352,82 @@ InputEvent *xev_as_input_event(XEvent &ev) {
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Returns a mask for the event_type
|
||||
/// @param event_type Xlib event type
|
||||
/// @return Xlib event mask
|
||||
int ev_to_mask(int event_type) {
|
||||
switch (event_type) {
|
||||
case KeyPress:
|
||||
return KeyPressMask;
|
||||
case KeyRelease:
|
||||
return KeyReleaseMask;
|
||||
case ButtonPress:
|
||||
return ButtonPressMask;
|
||||
case ButtonRelease:
|
||||
return ButtonReleaseMask;
|
||||
case EnterNotify:
|
||||
return EnterWindowMask;
|
||||
case LeaveNotify:
|
||||
return LeaveWindowMask;
|
||||
case MotionNotify:
|
||||
return PointerMotionMask;
|
||||
default:
|
||||
return NoEventMask;
|
||||
}
|
||||
}
|
||||
|
||||
void propagate_x11_event(XEvent &ev) {
|
||||
InputEvent *i_ev = xev_as_input_event(ev);
|
||||
/* forward the event to the desktop window */
|
||||
if (i_ev != nullptr) {
|
||||
i_ev->common.window = window.desktop;
|
||||
i_ev->common.x = i_ev->common.x_root;
|
||||
i_ev->common.y = i_ev->common.y_root;
|
||||
} else {
|
||||
if (i_ev == nullptr) {
|
||||
// Not a known input event; blindly propagating them causes loops and all
|
||||
// sorts of other evil.
|
||||
return;
|
||||
}
|
||||
DBGP2("Propagating event: { type: %d; serial: %d }", i_ev->type, i_ev->common.serial);
|
||||
XSendEvent(display, window.desktop, False, window.event_mask, &ev);
|
||||
|
||||
int _revert_to;
|
||||
Window focused;
|
||||
XGetInputFocus(display, &focused, &_revert_to);
|
||||
if (focused == window.window) {
|
||||
Time time = CurrentTime;
|
||||
if (i_ev != nullptr) { time = i_ev->common.time; }
|
||||
XSetInputFocus(display, window.desktop, RevertToPointerRoot, time);
|
||||
i_ev->common.window = window.desktop;
|
||||
i_ev->common.x = i_ev->common.x_root;
|
||||
i_ev->common.y = i_ev->common.y_root;
|
||||
i_ev->common.time = CurrentTime;
|
||||
|
||||
/* forward the event to the window below conky (e.g. caja) or desktop */
|
||||
{
|
||||
std::vector<Window> below = query_x11_windows_at_pos(
|
||||
display, i_ev->common.x_root, i_ev->common.y_root,
|
||||
[](XWindowAttributes &a) { return a.map_state == IsViewable; });
|
||||
auto it = std::remove_if(below.begin(), below.end(),
|
||||
[](Window w) { return w == window.window; });
|
||||
below.erase(it, below.end());
|
||||
if (!below.empty()) {
|
||||
i_ev->common.window = below.back();
|
||||
|
||||
Window _ignore;
|
||||
// Update event x and y coordinates to be target window relative
|
||||
XTranslateCoordinates(display, window.root, i_ev->common.window,
|
||||
i_ev->common.x_root, i_ev->common.y_root,
|
||||
&i_ev->common.x, &i_ev->common.y, &_ignore);
|
||||
}
|
||||
// drop below vector
|
||||
}
|
||||
|
||||
XUngrabPointer(display, CurrentTime);
|
||||
XSendEvent(display, i_ev->common.window, False, ev_to_mask(i_ev->type), &ev);
|
||||
}
|
||||
|
||||
#ifdef BUILD_MOUSE_EVENTS
|
||||
// Assuming parent has a simple linear stack of descendants, this function
|
||||
// returns the last leaf on the graph.
|
||||
inline Window last_descendant(Display *display, Window parent) {
|
||||
/// @brief This function returns the last descendant of a window (leaf) on the
|
||||
/// graph.
|
||||
///
|
||||
/// This function assumes the window stack below `parent` is linear. If it
|
||||
/// isn't, it's only guaranteed that _some_ descendant of `parent` will be
|
||||
/// returned. If provided `parent` has no descendants, the `parent` is returned.
|
||||
Window query_x11_last_descendant(Display *display, Window parent) {
|
||||
Window _ignored, *children;
|
||||
std::uint32_t count;
|
||||
|
||||
Window current = parent;
|
||||
|
||||
while (
|
||||
XQueryTree(display, current, &_ignored, &_ignored, &children, &count) &&
|
||||
count != 0) {
|
||||
while (XQueryTree(display, current, &_ignored, &_ignored, &children,
|
||||
&count) == Success &&
|
||||
count != 0) {
|
||||
current = children[count - 1];
|
||||
XFree(children);
|
||||
}
|
||||
@ -1396,10 +1435,84 @@ inline Window last_descendant(Display *display, Window parent) {
|
||||
return current;
|
||||
}
|
||||
|
||||
std::vector<Window> query_x11_windows(Display *display) {
|
||||
// _NET_CLIENT_LIST_STACKING
|
||||
Window root = DefaultRootWindow(display);
|
||||
|
||||
Atom clients_atom = XInternAtom(display, "_NET_CLIENT_LIST_STACKING", 0);
|
||||
|
||||
Atom actual_type;
|
||||
int actual_format;
|
||||
unsigned long nitems;
|
||||
unsigned long bytes_after;
|
||||
unsigned char *data = nullptr;
|
||||
|
||||
// try retrieving ordered windows first:
|
||||
if (XGetWindowProperty(display, root, clients_atom, 0, 0, False, XA_WINDOW,
|
||||
&actual_type, &actual_format, &nitems, &bytes_after,
|
||||
&data) == Success) {
|
||||
free(data);
|
||||
size_t count = bytes_after / 4;
|
||||
|
||||
if (XGetWindowProperty(display, root, clients_atom, 0, bytes_after / 4,
|
||||
False, XA_WINDOW, &actual_type, &actual_format,
|
||||
&nitems, &bytes_after, &data) == Success) {
|
||||
Window *wdata = reinterpret_cast<Window *>(data);
|
||||
std::vector<Window> result(wdata, wdata + nitems);
|
||||
free(data);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
clients_atom = XInternAtom(display, "_NET_CLIENT_LIST", 0);
|
||||
if (XGetWindowProperty(display, root, clients_atom, 0, 0, False, XA_WINDOW,
|
||||
&actual_type, &actual_format, &nitems, &bytes_after,
|
||||
&data) == Success) {
|
||||
free(data);
|
||||
size_t count = bytes_after / 4;
|
||||
|
||||
if (XGetWindowProperty(display, root, clients_atom, 0, count, False,
|
||||
XA_WINDOW, &actual_type, &actual_format, &nitems,
|
||||
&bytes_after, &data) == Success) {
|
||||
Window *wdata = reinterpret_cast<Window *>(data);
|
||||
std::vector<Window> result(wdata, wdata + nitems);
|
||||
free(data);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// slowest method that also returns inaccurate results:
|
||||
|
||||
// TODO: How do we remove window decorations and other unwanted WM/DE junk
|
||||
// from this?
|
||||
|
||||
std::vector<Window> result;
|
||||
std::vector<Window> queue = {root};
|
||||
|
||||
Window _ignored, *children;
|
||||
std::uint32_t count;
|
||||
|
||||
while (!queue.empty()) {
|
||||
Window current = queue.back();
|
||||
queue.pop_back();
|
||||
if (XQueryTree(display, current, &_ignored, &_ignored, &children, &count) ==
|
||||
Success &&
|
||||
count != 0) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
queue.push_back(children[i]);
|
||||
result.push_back(current);
|
||||
}
|
||||
XFree(children);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Window query_x11_window_at_pos(Display *display, int x, int y) {
|
||||
Window root = DefaultRootWindow(display);
|
||||
|
||||
// these values are ignored but NULL can't be passed
|
||||
// these values are ignored but NULL can't be passed to XQueryPointer.
|
||||
Window root_return;
|
||||
int root_x_return, root_y_return, win_x_return, win_y_return;
|
||||
unsigned int mask_return;
|
||||
@ -1408,13 +1521,31 @@ Window query_x11_window_at_pos(Display *display, int x, int y) {
|
||||
XQueryPointer(display, window.root, &root_return, &last, &root_x_return,
|
||||
&root_y_return, &win_x_return, &win_y_return, &mask_return);
|
||||
|
||||
// If root, last descendant will be wrong
|
||||
if (last == 0) return 0;
|
||||
|
||||
// X11 correctly returns a window which covers conky area, but returned
|
||||
// window is not window.window, but instead a parent node in some cases and
|
||||
// the window.window we want to check for is a 1x1 child of that window.
|
||||
return last_descendant(display, last);
|
||||
if (last == 0) return root;
|
||||
return last;
|
||||
}
|
||||
|
||||
#endif /* BUILD_MOUSE_EVENTS */
|
||||
std::vector<Window> query_x11_windows_at_pos(
|
||||
Display *display, int x, int y,
|
||||
std::function<bool(XWindowAttributes &)> predicate) {
|
||||
std::vector<Window> result;
|
||||
|
||||
Window root = DefaultRootWindow(display);
|
||||
XWindowAttributes attr;
|
||||
|
||||
for (Window current : query_x11_windows(display)) {
|
||||
int pos_x, pos_y;
|
||||
Window _ignore;
|
||||
// Doesn't account for decorations. There's no sane way to do that.
|
||||
XTranslateCoordinates(display, current, root, 0, 0, &pos_x, &pos_y,
|
||||
&_ignore);
|
||||
XGetWindowAttributes(display, current, &attr);
|
||||
|
||||
if (pos_x <= x && pos_y <= y && pos_x + attr.width >= x &&
|
||||
pos_y + attr.height >= y && predicate(attr)) {
|
||||
result.push_back(current);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
58
src/x11.h
58
src/x11.h
@ -40,6 +40,8 @@
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#ifdef BUILD_ARGB
|
||||
/* true if use_argb_visual=true and argb visual was found*/
|
||||
@ -74,7 +76,12 @@ enum window_hints {
|
||||
extern Display *display;
|
||||
|
||||
struct conky_x11_window {
|
||||
Window root, window, desktop;
|
||||
/// XID of x11 root window
|
||||
Window root;
|
||||
/// XID of Conky window
|
||||
Window window;
|
||||
/// XID of DE desktop window (or root if none)
|
||||
Window desktop;
|
||||
Drawable drawable;
|
||||
Visual *visual;
|
||||
Colormap colourmap;
|
||||
@ -152,9 +159,54 @@ union InputEvent {
|
||||
InputEvent *xev_as_input_event(XEvent &ev);
|
||||
void propagate_x11_event(XEvent &ev);
|
||||
|
||||
#ifdef BUILD_MOUSE_EVENTS
|
||||
/// @brief Tries getting a list of windows ordered from bottom to top.
|
||||
///
|
||||
/// Whether the list is correctly ordered depends on WM/DE providing the
|
||||
/// `_NET_CLIENT_LIST_STACKING` atom. If only `_NET_CLIENT_LIST` is defined,
|
||||
/// this function assumes the WM/DE is a tiling one without stacking order.
|
||||
///
|
||||
/// If neither of the atoms are provided, this function tries traversing the
|
||||
/// window graph in order to collect windows. In this case, map state of windows
|
||||
/// is ignored. This also produces a lot of noise for some WM/DEs due to
|
||||
/// inserted window decorations.
|
||||
///
|
||||
/// @param display which display to query for windows @return a (likely) ordered
|
||||
/// list of windows
|
||||
std::vector<Window> query_x11_windows(Display *display);
|
||||
|
||||
/// @brief Finds the last descendant of a window (leaf) on the graph.
|
||||
///
|
||||
/// This function assumes the window stack below `parent` is linear. If it
|
||||
/// isn't, it's only guaranteed that _some_ descendant of `parent` will be
|
||||
/// returned. If provided `parent` has no descendants, the `parent` is returned.
|
||||
///
|
||||
/// @param display display of parent
|
||||
/// @return a descendant window
|
||||
Window query_x11_last_descendant(Display *display, Window parent);
|
||||
|
||||
/// @brief Returns the top-most window overlapping provided screen coordinates.
|
||||
///
|
||||
/// @param display display of parent
|
||||
/// @param x screen X position contained by window
|
||||
/// @param y screen Y position contained by window
|
||||
/// @return a top-most window at provided screen coordinates, or root
|
||||
Window query_x11_window_at_pos(Display *display, int x, int y);
|
||||
#endif /* BUILD_MOUSE_EVENTS */
|
||||
|
||||
/// @brief Returns a list of windows overlapping provided screen coordinates.
|
||||
///
|
||||
/// Vector returned by this function will never contain root because it's
|
||||
/// assumed to always cover the entire display.
|
||||
///
|
||||
/// @param display display of parent
|
||||
/// @param x screen X position contained by window
|
||||
/// @param y screen Y position contained by window
|
||||
/// @param predicate any additional predicates to apply for XWindowAttributes
|
||||
/// (besides bounds testing).
|
||||
/// @return a vector of windows at provided screen coordinates
|
||||
std::vector<Window> query_x11_windows_at_pos(
|
||||
Display *display, int x, int y,
|
||||
std::function<bool(XWindowAttributes &)> predicate =
|
||||
[](XWindowAttributes &a) { return true; });
|
||||
|
||||
#ifdef BUILD_XDBE
|
||||
void xdbe_swap_buffers(void);
|
||||
|
Loading…
Reference in New Issue
Block a user