diff --git a/src/display-x11.cc b/src/display-x11.cc index d4b0152ac..e51cd3a5d 100644 --- a/src/display-x11.cc +++ b/src/display-x11.cc @@ -71,6 +71,12 @@ #include "logging.h" #include "x11.h" +// Use display, screen and window globals from x11.h instead +// This is here to avoid issues with virtual roots +#undef RootWindowOfScreen +#undef RootWindow +#undef DefaultRootWindow + // TODO: cleanup externs (move to conky.h ?) #ifdef OWN_WINDOW extern int fixed_size, fixed_pos; @@ -477,22 +483,22 @@ bool handle_event( } device_info = device_info::from_xi_id(data->deviceid, data->display); - Window event_window; - modifier_state_t mods; - if (data->evtype == XI_Motion || data->evtype == XI_ButtonPress || - data->evtype == XI_ButtonRelease) { - event_window = query_x11_window_at_pos(display, data->root_x, data->root_y); - // query_result is not window.window in some cases. - event_window = query_x11_last_descendant(display, event_window); - mods = x11_modifier_state(data->mods.effective); + if (!(data->evtype == XI_Motion || data->evtype == XI_ButtonPress || + data->evtype == XI_ButtonRelease)) { + return true; } - bool cursor_over_conky = - (event_window == window.window || - window.window == 0L && - (event_window == window.root || event_window == window.desktop)) && - (data->root_x >= window.x && data->root_x < (window.x + window.width) && - data->root_y >= window.y && data->root_y < (window.y + window.height)); + Window event_window = + query_x11_window_at_pos(display, data->root_x, data->root_y); + // query_result is not window.window in some cases. + modifier_state_t mods = x11_modifier_state(data->mods.effective); + + bool same_window = query_x11_top_level(display, event_window) == + query_x11_top_level(display, window.window); + bool cursor_over_conky = same_window && data->root_x >= window.x && + data->root_x < (window.x + window.width) && + data->root_y >= window.y && + data->root_y < (window.y + window.height); // XInput reports events twice on some hardware (even by 'xinput --test-xi2') auto hash = std::make_tuple(data->serial, data->evtype, data->event); diff --git a/src/gui.cc b/src/gui.cc index 3c388eaac..f39cbe475 100644 --- a/src/gui.cc +++ b/src/gui.cc @@ -52,11 +52,6 @@ int workarea[4]; /* Window stuff */ char window_created = 0; -/* local prototypes */ -#ifdef BUILD_X11 -void x11_init_window(lua::state &l, bool own); -#endif /*BUILD_X11*/ - /********************* ************************/ bool out_to_gui(lua::state &l) { @@ -88,7 +83,24 @@ void own_window_setting::lua_setter(lua::state &l, bool init) { if (out_to_gui(l)) { #ifdef BUILD_X11 - x11_init_window(l, do_convert(l, -1).first); + x11_window_options options; + +#ifdef OWN_WINDOW + options.use_own_window = do_convert(l, -1).first; + options.window_ty = own_window_type.get(l); + options.window_hints = own_window_hints.get(l); + options.window_title = own_window_title.get(l); + options.window_class = own_window_class.get(l); + + options.inner_margin = border_inner_margin.get(l); + options.border_thickness = border_width.get(l); + options.outer_margin = border_outer_margin.get(l); +#ifdef BUILD_ARGB + options.argb_visual = use_argb_visual.get(l); +#endif /* BUILD_ARGB */ +#endif /* OWN_WINDOW */ + + x11_init_window(options); #endif /*BUILD_X11*/ } else { // own_window makes no sense when not drawing to X diff --git a/src/mouse-events.cc b/src/mouse-events.cc index 864b1081a..5adc16d5a 100644 --- a/src/mouse-events.cc +++ b/src/mouse-events.cc @@ -248,10 +248,10 @@ device_info *device_info::from_xi_id(xi_device_id device_id, Display *display) { } void handle_xi_device_change(const XIHierarchyEvent *event) { - if (event->flags & XISlaveRemoved != 0) { + if ((event->flags & XISlaveRemoved) != 0) { for (int i = 0; i < event->num_info; i++) { auto info = event->info[i]; - if (info.flags & XISlaveRemoved != 0 && + if ((info.flags & XISlaveRemoved) != 0 && xi_id_mapping.count(info.deviceid) > 0) { size_t id = xi_id_mapping[info.deviceid]; xi_id_mapping.erase(info.deviceid); @@ -275,22 +275,25 @@ size_t fixed_valuator_index(Display *display, XIDeviceInfo *device, int format_return; unsigned long num_items; unsigned long bytes_after; - if (XIGetProperty(display, device->deviceid, override_atom, 0, 1, False, - XA_INTEGER, &type_return, &format_return, &num_items, - &bytes_after, - reinterpret_cast(&value)) == Success) { - if (type_return != XA_INTEGER || num_items > 1) { - NORM_ERR( - "invalid '%s' option value, expected a single integer; value will be " - "ignored", - atom_names[valuator]); + do { + if (XIGetProperty(display, device->deviceid, override_atom, 0, 1, False, + XA_INTEGER, &type_return, &format_return, &num_items, + &bytes_after, + reinterpret_cast(&value)) == Success) { + if (num_items == 0) break; + if (type_return != XA_INTEGER) { + NORM_ERR( + "invalid '%s' option value, expected a single integer; value will " + "be ignored", + atom_names[valuator]); + XFree(value); + break; + } + uint32_t result = *reinterpret_cast(value); XFree(value); - return valuator; + return static_cast(result); } - uint32_t result = *reinterpret_cast(value); - XFree(value); - return static_cast(result); - } + } while (true); return valuator; } @@ -300,46 +303,55 @@ bool fixed_valuator_relative(Display *display, XIDeviceInfo *device, valuator_t valuator, XIValuatorClassInfo *class_info) { const std::array atom_names = { - "ConkyValuatorMoveType", - "ConkyValuatorScrollType", + "ConkyValuatorMoveMode", + "ConkyValuatorScrollMode", }; - Atom override_atom = XInternAtom(display, atom_names[valuator >> 1], False); - unsigned char *value; + + Atom override_atom = XInternAtom(display, atom_names[valuator >> 1], True); + unsigned char *value_return; Atom type_return; int format_return; unsigned long num_items; unsigned long bytes_after; - if (XIGetProperty(display, device->deviceid, override_atom, 0, 9, False, - XA_STRING, &type_return, &format_return, &num_items, - &bytes_after, - reinterpret_cast(&value)) == Success) { - if (type_return != XA_STRING) { - NORM_ERR( - "invalid '%s' option value, expected a string; value will be " - "ignored", - atom_names[valuator >> 1]); - XFree(value); - return class_info->type == XIModeRelative; - } - // lowercase value - for (auto c = value; *c; ++c) *c = tolower(*c); - - bool relative = false; - if (strcmp(reinterpret_cast(value), "relative") == 0) { - relative = true; - } else if (strcmp(reinterpret_cast(value), "absolute") != 0) { - NORM_ERR( - "unknown '%s' option value: '%s', expected 'absolute' or 'relative'; " - "value will be ignored", - atom_names[static_cast(valuator) >> 1]); + do { + if (XIGetProperty( + display, device->deviceid, override_atom, 0, 9, False, XA_ATOM, + &type_return, &format_return, &num_items, &bytes_after, + reinterpret_cast(&value_return)) == Success) { + if (num_items == 0) break; + if (type_return != XA_ATOM) { + NORM_ERR( + "invalid '%s' option value, expected an atom (string); value will " + "be ignored", + atom_names[valuator >> 1]); + XFree(value_return); + break; + } + Atom return_atom = *reinterpret_cast(value_return); + XFree(value_return); + char *value = XGetAtomName(display, return_atom); + + // lowercase value + for (auto c = value; *c; ++c) *c = tolower(*c); + + bool relative = false; + if (strcmp(reinterpret_cast(value), "relative") == 0) { + relative = true; + } else if (strcmp(reinterpret_cast(value), "absolute") != 0) { + NORM_ERR( + "unknown '%s' option value: '%s', expected 'absolute' or " + "'relative'; " + "value will be ignored", + atom_names[valuator >> 1]); + XFree(value); + break; + } XFree(value); - return class_info->type == XIModeRelative; + return relative; } - XFree(value); - return relative; - } - return class_info->type == XIModeRelative; + } while (true); + return class_info->mode == XIModeRelative; } void device_info::init_xi_device( @@ -386,7 +398,6 @@ void device_info::init_xi_device( }; this->valuators[valuator] = info; - DBGP2("SToRING: %s %d", name.c_str(), info.index); } if (std::holds_alternative(source)) { @@ -440,9 +451,31 @@ xi_event_data *xi_event_data::read_cookie(Display *display, .valuators = valuators, .mods = source->mods, .group = source->group, + .valuators_relative = {0.0, 0.0, 0.0, 0.0}, }; XFreeEventData(display, cookie); + // Precompute relative values if they're absolute + auto device = device_info::from_xi_id(result->deviceid, result->display); + if (device == nullptr) return result; // shouldn't happen + for (size_t v = 0; v < valuator_t::VALUATOR_COUNT; v++) { + valuator_t valuator = static_cast(v); + auto valuator_info = device->valuator(valuator); + + if (result->valuators.count(valuator_info.index) == 0) { continue; } + auto current = result->valuators[valuator_info.index]; + + if (valuator_info.relative) { + result->valuators_relative[v] = current; + } else { + // XXX these doubles come from int values and might wrap around though + // it's hard to tell what int type is the source as it depends on the + // device/driver. + result->valuators_relative[v] = current - valuator_info.value; + } + valuator_info.value = current; + } + return result; } @@ -466,20 +499,7 @@ std::optional xi_event_data::valuator_value(valuator_t valuator) const { std::optional xi_event_data::valuator_relative_value( valuator_t valuator) const { - auto current = this->valuator_value(valuator); - if (!current.has_value()) return std::nullopt; - - auto valuator_info = this->valuator_info(valuator); - if (valuator_info == nullptr) return std::nullopt; - - if (valuator_info->relative) { - return current.value(); - } else { - // XXX these doubles come from int values and might wrap around though it's - // hard to tell what int type is the source as it depends on the - // device/driver. - return current.value() - valuator_info->value; - } + return this->valuators_relative.at(valuator); } std::vector> xi_event_data::generate_events( diff --git a/src/mouse-events.h b/src/mouse-events.h index 0af245c35..3c55358cc 100644 --- a/src/mouse-events.h +++ b/src/mouse-events.h @@ -241,7 +241,6 @@ struct mouse_crossing_event : public mouse_positioned_event { }; #ifdef BUILD_XINPUT - typedef int xi_device_id; typedef int xi_event_type; @@ -282,8 +281,6 @@ struct xi_event_data { Time time; xi_device_id deviceid; int sourceid; - /// Primary event detail. Meaning depends on `evtype` value: - /// XI_ButtonPress - Mouse button int detail; Window root; Window event; @@ -299,6 +296,11 @@ struct xi_event_data { XIModifierState mods; XIGroupState group; + // Extra data + + /// Precomputed relative values + std::array valuators_relative; + static xi_event_data *read_cookie(Display *display, XGenericEventCookie *cookie); diff --git a/src/x11.cc b/src/x11.cc index 68ac7ae3d..c0d899ac3 100644 --- a/src/x11.cc +++ b/src/x11.cc @@ -105,7 +105,6 @@ conky::simple_config_setting display_name("display", std::string(), /* local prototypes */ static void update_workarea(); -static Window find_desktop_window(Window *p_root, Window *p_desktop); static Window find_subwindow(Window win, int w, int h); static void init_x11(); @@ -311,6 +310,57 @@ __attribute__((noreturn)) static int x11_ioerror_handler(Display *d) { CRIT_ERR("X IO Error: Display %lx\n", reinterpret_cast(d)); } +/// @brief Function to get virtual root windows of screen. +/// +/// Some WMs (swm, tvtwm, amiwm, enlightenment, etc.) use virtual roots to +/// manage workspaces. These are direct descendants of root and WMs reparent all +/// children to them. +/// +/// @param screen screen to get the (current) virtual root of @return the +/// virtual root window of the screen +static Window VirtualRootWindowOfScreen(Screen *screen) { + Window root = screen->root; + Display *dpy = screen->display; + + Window rootReturn, parentReturn, *children; + unsigned int numChildren; + Atom actual_type; + int actual_format; + unsigned long nitems, bytesafter; + + /* go look for a virtual root */ + Atom __SWM_VROOT = ATOM(__SWM_VROOT); + if (XQueryTree(dpy, root, &rootReturn, &parentReturn, &children, + &numChildren)) { + for (int i = 0; i < numChildren; i++) { + Window *newRoot = None; + + if (XGetWindowProperty( + dpy, children[i], __SWM_VROOT, 0, 1, False, XA_WINDOW, + &actual_type, &actual_format, &nitems, &bytesafter, + reinterpret_cast(&newRoot)) == Success && + newRoot != None) { + root = *newRoot; + break; + } + } + if (children) XFree((char *)children); + } + + return root; +} + +// Polyfill overwrite default Xlib definitions to account for virtual root +// windows +#undef RootWindowOfScreen +#define RootWindowOfScreen(s) VirtualRootWindowOfScreen(s) +#undef RootWindow +#define RootWindow(dpy, screen) \ + VirtualRootWindowOfScreen(ScreenOfDisplay(dpy, screen)) +#undef DefaultRootWindow +#define DefaultRootWindow(dpy) \ + VirtualRootWindowOfScreen(DefaultScreenOfDisplay(dpy)) + /* X11 initializer */ static void init_x11() { DBGP("enter init_x11()"); @@ -420,75 +470,21 @@ static void update_workarea() { * Return desktop window on success, * and set root and desktop byref return values. * Return 0 on failure. */ -static Window find_desktop_window(Window *p_root, Window *p_desktop) { - Atom type; - int format, i; - unsigned long nitems, bytes; - unsigned int n; - if (!display) return 0; - Window root = RootWindow(display, screen); - Window win; - Window troot, parent, *children; - unsigned char *buf = nullptr; - - if ((p_root == nullptr) || (p_desktop == nullptr)) { return 0; } - - /* some window managers set __SWM_VROOT to some child of root window */ - - XQueryTree(display, root, &troot, &parent, &children, &n); - for (i = 0; i < static_cast(n); i++) { - if (XGetWindowProperty(display, children[i], ATOM(__SWM_VROOT), 0, 1, False, - XA_WINDOW, &type, &format, &nitems, &bytes, - &buf) == Success && - type == XA_WINDOW) { - win = *reinterpret_cast(buf); - XFree(buf); - XFree(children); - fprintf(stderr, - PACKAGE_NAME - ": desktop window (%lx) found from __SWM_VROOT property\n", - win); - fflush(stderr); - *p_root = win; - *p_desktop = win; - return win; - } - - if (buf != nullptr) { - XFree(buf); - buf = nullptr; - } - } - XFree(children); +static Window find_desktop_window(Window root) { + Window desktop = root; /* get subwindows from root */ - win = find_subwindow(root, -1, -1); - + desktop = find_subwindow(root, -1, -1); update_workarea(); + desktop = find_subwindow(desktop, workarea[2], workarea[3]); - win = find_subwindow(win, workarea[2], workarea[3]); - - if (buf != nullptr) { - XFree(buf); - buf = nullptr; - } - - if (win != root) { - fprintf(stderr, - PACKAGE_NAME - ": desktop window (%lx) is subwindow of root window (%lx)\n", - win, root); + if (desktop != root) { + DBGP2("desktop window (0x%lx) is subwindow of root window (0x%lx)", desktop, + root); } else { - fprintf(stderr, PACKAGE_NAME ": desktop window (%lx) is root window\n", - win); + DBGP2("desktop window (0x%lx) is root window", desktop); } - - fflush(stderr); - - *p_root = root; - *p_desktop = win; - - return win; + return desktop; } #ifdef OWN_WINDOW @@ -578,27 +574,31 @@ void destroy_window() { memset(&window, 0, sizeof(struct conky_x11_window)); } -void x11_init_window(lua::state &l, bool own) { +void x11_init_window(x11_window_options options) { DBGP("enter x11_init_window()"); - // own is unused if OWN_WINDOW is not defined - (void)own; + + window.root = RootWindow(display, screen); + if (window.root == None) { + NORM_ERR("no root window found"); + return; + } + window.desktop = find_desktop_window(window.root); #ifdef OWN_WINDOW - if (own) { + bool own_window = options.use_own_window; + window_type own_window_type = options.window_ty; + + window.visual = DefaultVisual(display, screen); + window.colourmap = DefaultColormap(display, screen); + + if (own_window) { int depth = 0, flags = CWOverrideRedirect | CWBackingStore; Visual *visual = nullptr; - if (find_desktop_window(&window.root, &window.desktop) == 0U) { - DBGP2("no desktop window found"); - return; - } - - window.visual = DefaultVisual(display, screen); - window.colourmap = DefaultColormap(display, screen); depth = CopyFromParent; visual = CopyFromParent; #ifdef BUILD_ARGB - if (use_argb_visual.get(l) && (get_argb_visual(&visual, &depth) != 0)) { + if (options.argb_visual && (get_argb_visual(&visual, &depth) != 0)) { have_argb_visual = true; window.visual = visual; window.colourmap = XCreateColormap(display, DefaultRootWindow(display), @@ -606,26 +606,17 @@ void x11_init_window(lua::state &l, bool own) { } #endif /* BUILD_ARGB */ - int b = border_inner_margin.get(l) + border_width.get(l) + - border_outer_margin.get(l); - + int border = + options.inner_margin + options.border_thickness + options.outer_margin; /* Sanity check to avoid making an invalid 0x0 window */ - if (b == 0) { b = 1; } - - XClassHint classHint; - - // class_name must be a named local variable, so that c_str() remains - // valid until we call XmbSetWMProperties() or XSetClassHint. We use - // const_cast because, for whatever reason, res_name is not declared as - // const char *. XmbSetWMProperties hopefully doesn't modify the value - // (hell, even their own example app assigns a literal string constant to - // the field) - const std::string &class_name = own_window_class.get(l); + if (border == 0) { border = 1; } - classHint.res_name = const_cast(class_name.c_str()); - classHint.res_class = classHint.res_name; + XClassHint classHint = XClassHint{ + .res_name = const_cast(options.window_title.c_str()), + .res_class = const_cast(options.window_class.c_str()), + }; - if (own_window_type.get(l) == TYPE_OVERRIDE) { + if (own_window_type == TYPE_OVERRIDE) { /* An override_redirect True window. * No WM hints or button processing needed. */ XSetWindowAttributes attrs = {ParentRelative, @@ -654,15 +645,15 @@ void x11_init_window(lua::state &l, bool own) { /* Parent is desktop window (which might be a child of root) */ window.window = - XCreateWindow(display, window.desktop, window.x, window.y, b, b, 0, - depth, InputOutput, visual, flags, &attrs); + XCreateWindow(display, window.desktop, window.x, window.y, border, + border, 0, depth, InputOutput, visual, flags, &attrs); XLowerWindow(display, window.window); XSetClassHint(display, window.window, &classHint); fprintf(stderr, PACKAGE_NAME ": window type - override\n"); fflush(stderr); - } else { /* own_window_type.get(l) != TYPE_OVERRIDE */ + } else { /* own_window_type != TYPE_OVERRIDE */ /* A window managed by the window manager. * Process hints and buttons. */ @@ -680,7 +671,7 @@ void x11_init_window(lua::state &l, bool own) { StructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask, 0L, - own_window_type.get(l) == TYPE_UTILITY ? True : False, + own_window_type == TYPE_UTILITY ? True : False, 0, 0}; @@ -696,20 +687,19 @@ void x11_init_window(lua::state &l, bool own) { } #endif /* BUILD_ARGB */ - if (own_window_type.get(l) == TYPE_DOCK) { window.x = window.y = 0; } + if (own_window_type == TYPE_DOCK) { window.x = window.y = 0; } /* Parent is root window so WM can take control */ window.window = - XCreateWindow(display, window.root, window.x, window.y, b, b, 0, - depth, InputOutput, visual, flags, &attrs); - - uint16_t hints = own_window_hints.get(l); + XCreateWindow(display, window.root, window.x, window.y, border, + border, 0, depth, InputOutput, visual, flags, &attrs); wmHint.flags = InputHint | StateHint; /* allow decorated windows to be given input focus by WM */ - wmHint.input = TEST_HINT(hints, HINT_UNDECORATED) ? False : True; + wmHint.input = + TEST_HINT(options.window_hints, HINT_UNDECORATED) ? False : True; #ifdef BUILD_XSHAPE #ifdef BUILD_XFIXES - if (own_window_type.get(l) == TYPE_UTILITY) { + if (own_window_type == TYPE_UTILITY) { XRectangle rect; XserverRegion region = XFixesCreateRegion(display, &rect, 1); XFixesSetWindowShapeRegion(display, window.window, ShapeInput, 0, 0, @@ -724,18 +714,16 @@ void x11_init_window(lua::state &l, bool own) { if (XShapeQueryVersion(display, &major_version, &minor_version) == 0) { NORM_ERR("Input shapes are not supported"); } else { - if (own_window.get(*state) && - (own_window_type.get(*state) != TYPE_NORMAL || - ((TEST_HINT(own_window_hints.get(*state), HINT_UNDECORATED)) != - 0))) { + if (own_window && + (own_window_type != TYPE_NORMAL || + ((TEST_HINT(options.window_hints, HINT_UNDECORATED)) != 0))) { XShapeCombineRectangles(display, window.window, ShapeInput, 0, 0, nullptr, 0, ShapeSet, Unsorted); } } } #endif /* BUILD_XSHAPE */ - if (own_window_type.get(l) == TYPE_DOCK || - own_window_type.get(l) == TYPE_PANEL) { + if (own_window_type == TYPE_DOCK || own_window_type == TYPE_PANEL) { wmHint.initial_state = WithdrawnState; } else { wmHint.initial_state = NormalState; @@ -743,7 +731,7 @@ void x11_init_window(lua::state &l, bool own) { XmbSetWMProperties(display, window.window, nullptr, nullptr, argv_copy, argc_copy, nullptr, &wmHint, &classHint); - XStoreName(display, window.window, own_window_title.get(l).c_str()); + XStoreName(display, window.window, options.window_title.c_str()); /* Sets an empty WM_PROTOCOLS property */ XSetWMProtocols(display, window.window, nullptr, 0); @@ -752,7 +740,7 @@ void x11_init_window(lua::state &l, bool own) { if ((xa = ATOM(_NET_WM_WINDOW_TYPE)) != None) { Atom prop; - switch (own_window_type.get(l)) { + switch (own_window_type) { case TYPE_DESKTOP: prop = ATOM(_NET_WM_WINDOW_TYPE_DESKTOP); fprintf(stderr, PACKAGE_NAME ": window type - desktop\n"); @@ -788,7 +776,7 @@ void x11_init_window(lua::state &l, bool own) { /* Set desired hints */ /* Window decorations */ - if (TEST_HINT(hints, HINT_UNDECORATED)) { + if (TEST_HINT(options.window_hints, HINT_UNDECORATED)) { /* fprintf(stderr, PACKAGE_NAME": hint - undecorated\n"); fflush(stderr); */ @@ -801,7 +789,7 @@ void x11_init_window(lua::state &l, bool own) { } /* Below other windows */ - if (TEST_HINT(hints, HINT_BELOW)) { + if (TEST_HINT(options.window_hints, HINT_UNDECORATED)) { /* fprintf(stderr, PACKAGE_NAME": hint - below\n"); fflush(stderr); */ @@ -825,7 +813,7 @@ void x11_init_window(lua::state &l, bool own) { } /* Above other windows */ - if (TEST_HINT(hints, HINT_ABOVE)) { + if (TEST_HINT(options.window_hints, HINT_ABOVE)) { /* fprintf(stderr, PACKAGE_NAME": hint - above\n"); fflush(stderr); */ @@ -849,7 +837,7 @@ void x11_init_window(lua::state &l, bool own) { } /* Sticky */ - if (TEST_HINT(hints, HINT_STICKY)) { + if (TEST_HINT(options.window_hints, HINT_STICKY)) { /* fprintf(stderr, PACKAGE_NAME": hint - sticky\n"); fflush(stderr); */ @@ -873,7 +861,7 @@ void x11_init_window(lua::state &l, bool own) { } /* Skip taskbar */ - if (TEST_HINT(hints, HINT_SKIP_TASKBAR)) { + if (TEST_HINT(options.window_hints, HINT_SKIP_TASKBAR)) { /* fprintf(stderr, PACKAGE_NAME": hint - skip_taskbar\n"); fflush(stderr); */ @@ -888,7 +876,7 @@ void x11_init_window(lua::state &l, bool own) { } /* Skip pager */ - if (TEST_HINT(hints, HINT_SKIP_PAGER)) { + if (TEST_HINT(options.window_hints, HINT_SKIP_PAGER)) { /* fprintf(stderr, PACKAGE_NAME": hint - skip_pager\n"); fflush(stderr); */ @@ -913,16 +901,7 @@ void x11_init_window(lua::state &l, bool own) { { XWindowAttributes attrs; - if (window.window == 0u) { - window.window = find_desktop_window(&window.root, &window.desktop); - } - if (window.window == 0u) { - DBGP2("no root window found"); - return; - } - - window.visual = DefaultVisual(display, screen); - window.colourmap = DefaultColormap(display, screen); + if (window.window == None) { window.window = window.desktop; } if (XGetWindowAttributes(display, window.window, &attrs) != 0) { window.width = attrs.width; @@ -939,15 +918,10 @@ void x11_init_window(lua::state &l, bool own) { int64_t input_mask = ExposureMask | PropertyChangeMask; #ifdef OWN_WINDOW - if (own_window.get(l)) { + if (own_window) { input_mask |= StructureNotifyMask | ButtonPressMask | ButtonReleaseMask; } #ifdef BUILD_MOUSE_EVENTS - /* it's not recommended to add event masks to special windows in X; causes a - * crash */ - if (own && own_window_type.get(l) != TYPE_DESKTOP) { - input_mask |= PointerMotionMask | ButtonPressMask | ButtonReleaseMask; - } bool xinput_ok = false; #ifdef BUILD_XINPUT // not a loop; substitutes goto with break - if checks fail @@ -972,7 +946,7 @@ void x11_init_window(lua::state &l, bool own) { XISetMask(mask_bytes, XI_HierarchyChanged); XISetMask(mask_bytes, XI_Motion); // Capture click events for "override" window type - if (!own) { + if (!own_window) { XISetMask(mask_bytes, XI_ButtonPress); XISetMask(mask_bytes, XI_ButtonRelease); } @@ -983,7 +957,7 @@ void x11_init_window(lua::state &l, bool own) { ev_masks[0].mask = mask_bytes; XISelectEvents(display, window.root, ev_masks, 1); - if (own) { + if (own_window) { XIClearMask(mask_bytes, XI_Motion); XISetMask(mask_bytes, XI_ButtonPress); XISetMask(mask_bytes, XI_ButtonRelease); @@ -997,9 +971,11 @@ void x11_init_window(lua::state &l, bool own) { xinput_ok = true; } while (false); #endif /* BUILD_XINPUT */ - // fallback to basic X11 enter/leave events if xinput fails to init - if (!xinput_ok && own && own_window_type.get(l) != TYPE_DESKTOP) { - input_mask |= EnterWindowMask | LeaveWindowMask; + // Fallback to basic X11 enter/leave events if xinput fails to init. + // It's not recommended to add event masks to special windows in X; causes a + // crash (thus own_window_type != TYPE_DESKTOP) + if (!xinput_ok && own_window && own_window_type != TYPE_DESKTOP) { + input_mask |= PointerMotionMask | EnterWindowMask | LeaveWindowMask; } #endif /* BUILD_MOUSE_EVENTS */ #endif /* OWN_WINDOW */ @@ -1134,12 +1110,12 @@ static inline void get_x11_desktop_current_name(const std::string &names) { } void get_x11_desktop_info(Display *current_display, Atom atom) { - Window root; static Atom atom_current, atom_number, atom_names; struct information *current_info = &info; XWindowAttributes window_attributes; - root = RootWindow(current_display, current_info->x11.monitor.current); + Window root = + ScreenOfDisplay(current_display, current_info->x11.monitor.current)->root; /* Check if we initialise else retrieve changed property */ if (atom == 0) { @@ -1366,16 +1342,6 @@ void print_mouse_speed(struct text_object *obj, char *p, snprintf(p, p_max_size, "%d%%", (110 - threshold)); } -InputEvent *xev_as_input_event(XEvent &ev) { - if (ev.type == KeyPress || ev.type == KeyRelease || ev.type == ButtonPress || - ev.type == ButtonRelease || ev.type == MotionNotify || - ev.type == EnterNotify || ev.type == LeaveNotify) { - return reinterpret_cast(&ev); - } else { - return nullptr; - } -} - /// @brief Returns a mask for the event_type /// @param event_type Xlib event type /// @return Xlib event mask @@ -1465,60 +1431,70 @@ void propagate_x11_event(XEvent &ev, const void *cookie) { } #endif - InputEvent *i_ev = xev_as_input_event(ev); - if (i_ev == nullptr) { + if (!(ev.type == KeyPress || ev.type == KeyRelease || + ev.type == ButtonPress || ev.type == ButtonRelease || + ev.type == MotionNotify || ev.type == EnterNotify || + ev.type == LeaveNotify)) { // Not a known input event; blindly propagating them causes loops and all // sorts of other evil. return; } + // Note that using ev.xbutton is the same as using any of the above events. + // It's only important we don't access fields that are not common to all of + // them. - 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; + ev.xbutton.window = window.desktop; + ev.xbutton.x = ev.xbutton.x_root; + ev.xbutton.y = ev.xbutton.y_root; + ev.xbutton.time = CurrentTime; /* forward the event to the window below conky (e.g. caja) or desktop */ { std::vector below = query_x11_windows_at_pos( - display, i_ev->common.x_root, i_ev->common.y_root, + display, ev.xbutton.x_root, ev.xbutton.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(); + ev.xbutton.window = below.back(); Window _ignore; // Update event x and y coordinates to be target window relative - XTranslateCoordinates(display, window.desktop, i_ev->common.window, - i_ev->common.x_root, i_ev->common.y_root, - &i_ev->common.x, &i_ev->common.y, &_ignore); + XTranslateCoordinates(display, window.root, ev.xbutton.window, + ev.xbutton.x_root, ev.xbutton.y_root, &ev.xbutton.x, + &ev.xbutton.y, &_ignore); } // drop below vector } + int mask = + ev_to_mask(ev.type, ev.type == ButtonRelease ? ev.xbutton.button : 0); XUngrabPointer(display, CurrentTime); - XSendEvent(display, i_ev->common.window, True, - ev_to_mask(i_ev->type, - ev.type == ButtonRelease ? i_ev->xbutton.button : 0), - &ev); + XSendEvent(display, ev.xbutton.window, True, mask, &ev); if (focus) { - XSetInputFocus(display, i_ev->common.window, RevertToParent, CurrentTime); + XSetInputFocus(display, ev.xbutton.window, RevertToParent, CurrentTime); } } -Window query_x11_last_descendant(Display *display, Window parent) { - Window _ignored, *children; - std::uint32_t count; +Window query_x11_top_level(Display *display, Window child) { + if (child == None) return child; + Window root = DefaultRootWindow(display); - Window current = parent; + Window ret_root, parent, *children; + std::uint32_t child_count; - while (XQueryTree(display, current, &_ignored, &_ignored, &children, - &count) == Success && - count != 0) { - current = children[count - 1]; - XFree(children); - } + Window current = child; + int i; + do { + if (XQueryTree(display, current, &ret_root, &parent, &children, + &child_count) == 0) { + break; + } + if (child_count != 0) XFree(children); + if (parent == root) break; + current = parent; + } while (true); return current; } @@ -1531,15 +1507,10 @@ std::vector x11_atom_window_list(Display *display, Window window, unsigned long bytes_after; unsigned char *data = nullptr; - if (XGetWindowProperty(display, window, atom, 0, 0, False, XA_WINDOW, + if (XGetWindowProperty(display, window, atom, 0, (~0L), False, XA_WINDOW, &actual_type, &actual_format, &nitems, &bytes_after, &data) == Success) { - XFree(data); - size_t count = bytes_after / 4; - - if (XGetWindowProperty(display, window, atom, 0, bytes_after / 4, False, - XA_WINDOW, &actual_type, &actual_format, &nitems, - &bytes_after, &data) == Success) { + if (actual_format == XA_WINDOW && nitems > 0) { Window *wdata = reinterpret_cast(data); std::vector result(wdata, wdata + nitems); XFree(data); @@ -1551,38 +1522,38 @@ std::vector x11_atom_window_list(Display *display, Window window, } std::vector query_x11_windows(Display *display) { - Window root = DefaultRootWindow(display); + Window root = DefaultScreenOfDisplay(display)->root; - Atom clients_atom = XInternAtom(display, "_NET_CLIENT_LIST_STACKING", 0); + Atom clients_atom = ATOM(_NET_CLIENT_LIST_STACKING); std::vector result = x11_atom_window_list(display, root, clients_atom); if (result.empty()) { return result; } - clients_atom = XInternAtom(display, "_NET_CLIENT_LIST", 0); + clients_atom = ATOM(_NET_CLIENT_LIST); result = x11_atom_window_list(display, root, clients_atom); if (result.empty()) { return result; } - // slowest method that also returns inaccurate results: + // slowest method - // TODO: How do we remove window decorations and other unwanted WM/DE junk - // from this? - - std::vector queue = {root}; + std::vector queue = {DefaultRootWindow(display)}; Window _ignored, *children; std::uint32_t count; + const auto has_wm_hints = [&](Window window) { + auto hints = XGetWMHints(display, window); + bool result = hints != NULL; + if (result) XFree(hints); + return result; + }; + 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); + if (XQueryTree(display, current, &_ignored, &_ignored, &children, &count)) { + for (size_t i = 0; i < count; i++) queue.push_back(children[i]); + if (has_wm_hints(current)) result.push_back(current); + if (count > 0) XFree(children); } } diff --git a/src/x11.h b/src/x11.h index d9347d816..b75aba0a2 100644 --- a/src/x11.h +++ b/src/x11.h @@ -24,6 +24,7 @@ #pragma once +#include "config.h" #include "setting.hh" #include @@ -41,7 +42,6 @@ #include #include -#include #include #ifdef BUILD_ARGB @@ -115,50 +115,43 @@ struct conky_x11_window { extern struct conky_x11_window window; extern conky::simple_config_setting display_name; +/// Parameters required for X11 window initialization. +struct x11_window_options { +#ifdef OWN_WINDOW + bool use_own_window = true; + window_type window_ty = window_type::TYPE_NORMAL; + uint16_t window_hints = 0; + std::string window_title = "Conky"; + std::string window_class = PACKAGE_NAME; + + uint32_t inner_margin = 3; + uint32_t border_thickness = 1; + uint32_t outer_margin = 1; + +#ifdef BUILD_ARGB + bool argb_visual = false; +#endif /* BUILD_ARGB */ +#endif /* OWN_WINDOW */ + + x11_window_options() = default; +}; + +void x11_init_window(x11_window_options options); void destroy_window(void); void create_gc(void); void set_transparent_background(Window win); void get_x11_desktop_info(Display *current_display, Atom atom); void set_struts(int); -void x11_init_window(lua::state &l, bool own); void deinit_x11(); -// Fields common to all X11 input events -struct InputEventCommon { - int type; /* event type */ - uint64_t serial; /* # of last request processed by server */ - Bool send_event; /* true if this came from a SendEvent request */ - Display *display; /* Display the event was read from */ - Window window; /* "event" window reported relative to */ - Window root; /* root window that the event occurred on */ - Window subwindow; /* child window */ - Time time; /* milliseconds */ - int32_t x, y; /* pointer x, y coordinates in event window */ - int32_t x_root, y_root; /* coordinates relative to root */ - uint32_t state; /* key or button mask */ -}; - -union InputEvent { - int type; // event type - - InputEventCommon common; - - // Discrete interfaces - XAnyEvent xany; // common event interface - XKeyEvent xkey; // KeyPress & KeyRelease events - XButtonEvent xbutton; // ButtonPress & ButtonRelease events - XMotionEvent xmotion; // MotionNotify event - XCrossingEvent xcrossing; // EnterNotify & LeaveNotify events - - // Ensures InputEvent matches memory layout of XEvent. - // Accessing base variant is as code smell. - XEvent base; -}; - -// Returns InputEvent pointer to provided XEvent is an input event; nullptr -// otherwise. -InputEvent *xev_as_input_event(XEvent &ev); -void propagate_x11_event(XEvent &ev, const void *cookie); +/// @brief Forwards argument event to the top-most window at event positon that +/// isn't conky. +/// +/// Calling this function is time sensitive as it will query window at event +/// position **at invocation time**. +/// @param event event to forward +/// @param cookie optional cookie data +void propagate_x11_event(XEvent &event, const void *cookie = nullptr); /// @brief Returns a list of window values for the given atom. /// @param display display with which the atom is associated @@ -183,15 +176,15 @@ std::vector x11_atom_window_list(Display *display, Window window, /// list of windows std::vector query_x11_windows(Display *display); -/// @brief Finds the last descendant of a window (leaf) on the graph. +/// @brief Finds the last ascendant of a window (trunk) before root. /// -/// 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. +/// If provided `child` is root or has no windows between root and itself, the +/// `child` is returned. /// /// @param display display of parent -/// @return a descendant window -Window query_x11_last_descendant(Display *display, Window parent); +/// @param child window whose parents to query +/// @return the top level ascendant window +Window query_x11_top_level(Display *display, Window child); /// @brief Returns the top-most window overlapping provided screen coordinates. ///