Improve X11 strut handling (#2190)
* Add WM/DE specific strut handling * Ignore alignment and compute ideal strut placement unless WM supports it well * Minor documentation and code improvements Signed-off-by: Tin Švagelj <tin.svagelj@live.com>
This commit is contained in:
@@ -11,6 +11,9 @@
|
||||
### Reformatted all code (SVN@1007)
|
||||
3d26a4880e92df2c6004d85c1be458e35c6bfc3a
|
||||
|
||||
# Spelling fixes
|
||||
b62e1158300f0644b304f2f8b7e8105ba09073b3
|
||||
|
||||
### outsource the whole template object machinery
|
||||
# Moves template variables code from core.c into template.c
|
||||
ff199355f66216600c4bdc6bec4743afe5b61470
|
||||
|
@@ -94,13 +94,13 @@ option(BUILD_EXTRAS "Build extras (includes syntax files for editors)" false)
|
||||
|
||||
option(BUILD_I18N "Enable if you want internationalization support" true)
|
||||
|
||||
option(BUILD_COLOUR_NAME_MAP "Include mappings of colour name -> RGB (e.g. red -> ff0000)" true)
|
||||
|
||||
if(BUILD_I18N)
|
||||
set(LOCALE_DIR "${CMAKE_INSTALL_PREFIX}/share/locale"
|
||||
CACHE STRING "Directory containing the locales")
|
||||
endif(BUILD_I18N)
|
||||
|
||||
option(BUILD_COLOUR_NAME_MAP "Include mappings of colour name -> RGB (e.g. red -> ff0000)" true)
|
||||
|
||||
# Some standard options
|
||||
set(SYSTEM_CONFIG_FILE "/etc/conky/conky.conf"
|
||||
CACHE STRING "Default system-wide Conky configuration file")
|
||||
@@ -158,6 +158,8 @@ cmake_dependent_option(
|
||||
false
|
||||
"OS_DARWIN" false)
|
||||
|
||||
option(ENABLE_RUNTIME_TWEAKS "Enable runtime environment checks for better system integration" true)
|
||||
|
||||
# Optional features etc
|
||||
option(BUILD_WLAN "Enable wireless support" false)
|
||||
|
||||
|
@@ -50,6 +50,8 @@
|
||||
|
||||
#cmakedefine OWN_WINDOW 1
|
||||
|
||||
#cmakedefine ENABLE_RUNTIME_TWEAKS 1
|
||||
|
||||
#cmakedefine BUILD_MOUSE_EVENTS 1
|
||||
|
||||
#cmakedefine BUILD_XDAMAGE 1
|
||||
|
@@ -475,13 +475,17 @@ values:
|
||||
for background, icons and desktop menu, in those cases it might be
|
||||
better to use `normal` or one of the below options, as those will cover
|
||||
conky when they're clicked on.
|
||||
- `dock` windows reserve space on the desktop, i.e. WMs will try their
|
||||
best to not place windows on top of them. They're the same as `desktop`
|
||||
in other respects, but render on top of `desktop` windows.
|
||||
- `dock` windows are placed on screen edges. They're the same as `desktop`
|
||||
in most respects, but render on top of `desktop` windows and below
|
||||
`normal` ones. Conky doesn't define struts for this window type, but
|
||||
some WMs might still implicitly avoid placing windows on top of it.
|
||||
- `panel` windows are similar to `dock` windows, but they also reserve
|
||||
space _along a desktop edge_ (like taskbars), preventing maximized
|
||||
windows from overlapping them. The edge is chosen based on the
|
||||
`alignment` setting.
|
||||
space _along a workarea edge_ (like taskbars), preventing maximized
|
||||
windows from overlapping them. The edge is chosen based on final conky
|
||||
position and size, and workarea dimensions, to ensure normal windows
|
||||
have most free space available. For WMs that "cut out" entirely covered
|
||||
screens from reserved area, the edge will be chosen based on `alignment`
|
||||
setting.
|
||||
- `utility` windows are persistent utility windows (e.g. a palette or
|
||||
toolbox). They appear on top of other windows (in the same group), but
|
||||
otherwise behave much like `normal` windows.
|
||||
|
@@ -743,11 +743,7 @@ int if_existing_iftest(struct text_object *obj) {
|
||||
}
|
||||
|
||||
int if_running_iftest(struct text_object *obj) {
|
||||
#ifdef __linux__
|
||||
if (!get_process_by_name(obj->data.s)) {
|
||||
#else
|
||||
if (((obj->data.s) != nullptr) && (system(obj->data.s) != 0)) {
|
||||
#endif
|
||||
if (!is_process_running(obj->data.s)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
|
124
src/conky.cc
124
src/conky.cc
@@ -37,6 +37,7 @@
|
||||
#include <clocale>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
@@ -1706,13 +1707,122 @@ bool is_on_battery() { // checks if at least one battery specified in
|
||||
|
||||
volatile sig_atomic_t g_sigterm_pending, g_sighup_pending, g_sigusr2_pending;
|
||||
|
||||
void log_system_details() {
|
||||
void get_system_details() {
|
||||
char *session_ty = getenv("XDG_SESSION_TYPE");
|
||||
char *session = getenv("GDMSESSION");
|
||||
char *desktop = getenv("XDG_CURRENT_DESKTOP");
|
||||
if (desktop != nullptr || session != nullptr) {
|
||||
NORM_ERR("'%s' %s session running '%s' desktop", session, session_ty,
|
||||
desktop);
|
||||
if (std::strcmp(session_ty, "x11") == 0) {
|
||||
info.system.session = conky::info::display_session::x11;
|
||||
} else if (std::strcmp(session_ty, "wayland") == 0) {
|
||||
info.system.session = conky::info::display_session::wayland;
|
||||
} else {
|
||||
info.system.session = conky::info::display_session::unknown;
|
||||
}
|
||||
|
||||
info.system.wm_name = getenv("XDG_CURRENT_DESKTOP");
|
||||
// Per spec, XDG_CURRENT_DESKTOP is a semicolon separated list. In practice,
|
||||
// nearly all WM/DEs simply define a single name.
|
||||
// Others below are non-standard:
|
||||
if (info.system.wm_name == nullptr) {
|
||||
info.system.wm_name = getenv("XDG_SESSION_DESKTOP");
|
||||
}
|
||||
if (info.system.wm_name == nullptr) {
|
||||
info.system.wm_name = getenv("DESKTOP_SESSION");
|
||||
}
|
||||
if (info.system.wm_name == nullptr) {
|
||||
info.system.wm_name = getenv("GDMSESSION");
|
||||
}
|
||||
|
||||
#ifdef ENABLE_RUNTIME_TWEAKS
|
||||
const auto is_wayland = [&]() {
|
||||
#ifndef BUILD_WAYLAND
|
||||
return info.system.session == conky::info::display_session::wayland;
|
||||
#else
|
||||
// ignore wayland WMs
|
||||
return false;
|
||||
#endif
|
||||
};
|
||||
|
||||
const auto is_session = [&](auto &&...names) {
|
||||
return ((std::strcmp(info.system.wm_name, names) == 0) || ...);
|
||||
};
|
||||
|
||||
// Only add is_wayland guard for WM/DE that will never support another display
|
||||
// session protocol. e.g. Budgie will (or has) switch(ed) to Wayland at some
|
||||
// point, but older versions may use X11, so it needs to be detected for both
|
||||
// X11 and Wayland.
|
||||
if (info.system.wm_name == nullptr) {
|
||||
goto unknown_session;
|
||||
} else if (is_session("GNOME")) {
|
||||
info.system.wm = conky::info::window_manager::mutter;
|
||||
} else if (is_session("GNOME Classic", "metacity")) {
|
||||
info.system.wm = conky::info::window_manager::metacity;
|
||||
} else if (is_session("MATE")) {
|
||||
info.system.wm = conky::info::window_manager::marco;
|
||||
} else if (is_session("XFCE", "XFCE4")) {
|
||||
info.system.wm = conky::info::window_manager::xfwm;
|
||||
} else if (is_session("KDE", "Plasma", "KDE Plasma")) {
|
||||
info.system.wm = conky::info::window_manager::kwin;
|
||||
} else if (is_session("LXDE", "LXQt")) {
|
||||
info.system.wm = conky::info::window_manager::openbox;
|
||||
} else if (is_session("Unity")) {
|
||||
info.system.wm = conky::info::window_manager::compiz;
|
||||
} else if (is_session("Cinnamon")) {
|
||||
info.system.wm = conky::info::window_manager::mutter; // Muffin → Mutter
|
||||
} else if (!is_wayland() && is_session("Openbox")) {
|
||||
// Openbox doesn't set any session name env variables; must be set manually
|
||||
info.system.wm = conky::info::window_manager::openbox;
|
||||
} else if (!is_wayland() && is_session("Fluxbox")) {
|
||||
info.system.wm = conky::info::window_manager::fluxbox;
|
||||
} else if (!is_wayland() && (is_session("i3", "i3wm"))) {
|
||||
info.system.wm = conky::info::window_manager::i3;
|
||||
} else if (is_wayland() && is_session("Hyprland")) {
|
||||
info.system.wm = conky::info::window_manager::hyprland;
|
||||
} else if (is_wayland() && is_session("Sway")) {
|
||||
info.system.wm = conky::info::window_manager::sway;
|
||||
} else if (!is_wayland() && is_session("bspwm")) {
|
||||
info.system.wm = conky::info::window_manager::bspwm;
|
||||
} else if (is_session("awesome")) {
|
||||
// some talks about adding Wayland support
|
||||
info.system.wm = conky::info::window_manager::awesome;
|
||||
} else if (!is_wayland() && is_session("dwm")) {
|
||||
info.system.wm = conky::info::window_manager::dwm;
|
||||
} else if (!is_wayland() && is_session("herbstluftwm")) {
|
||||
info.system.wm = conky::info::window_manager::herbstluftwm;
|
||||
} else if (!is_wayland() && is_session("qtile")) {
|
||||
info.system.wm = conky::info::window_manager::qtile;
|
||||
} else if (!is_wayland() && is_session("windowmaker")) {
|
||||
info.system.wm = conky::info::window_manager::windowmaker;
|
||||
} else if (is_wayland() && is_session("Wayfire")) {
|
||||
info.system.wm = conky::info::window_manager::wayfire;
|
||||
} else if (is_wayland() && is_session("River")) {
|
||||
info.system.wm = conky::info::window_manager::river;
|
||||
} else if (is_session("Budgie")) {
|
||||
info.system.wm = conky::info::window_manager::mutter; // Budgie → Mutter
|
||||
} else if (is_session("Deepin")) {
|
||||
info.system.wm = conky::info::window_manager::dde;
|
||||
} else if (is_session("Enlightenment", "E17")) {
|
||||
info.system.wm = conky::info::window_manager::enlightenment;
|
||||
} else {
|
||||
unknown_session:
|
||||
info.system.wm = conky::info::window_manager::unknown;
|
||||
|
||||
// probably a misconfigured system... let's attempt a few more things
|
||||
if (getenv("CINNAMON_VERSION") != nullptr) {
|
||||
info.system.wm_name = "Cinnamon";
|
||||
info.system.wm = conky::info::window_manager::mutter;
|
||||
}
|
||||
|
||||
// TODO: Doesn't work yet. Process information is not yet populated.
|
||||
if (is_process_running("openbox")) {
|
||||
info.system.wm_name = "Openbox";
|
||||
info.system.wm = conky::info::window_manager::openbox;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (info.system.wm_name != nullptr) {
|
||||
NORM_ERR("'%s' %s session running", info.system.wm_name, session_ty);
|
||||
} else {
|
||||
NORM_ERR("unknown %s session running", session_ty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1738,7 +1848,7 @@ void main_loop() {
|
||||
sigaddset(&newmask, SIGUSR1);
|
||||
#endif
|
||||
|
||||
log_system_details();
|
||||
get_system_details();
|
||||
|
||||
last_update_time = 0.0;
|
||||
next_update_time = get_time() - fmod(get_time(), active_update_interval());
|
||||
|
66
src/conky.h
66
src/conky.h
@@ -30,6 +30,7 @@
|
||||
#ifndef _conky_h_
|
||||
#define _conky_h_
|
||||
|
||||
#include <cstddef>
|
||||
#define __STDC_FORMAT_MACROS
|
||||
|
||||
#include "config.h"
|
||||
@@ -157,6 +158,52 @@ char **get_templates(void);
|
||||
* needed by conky.c, linux.c and freebsd.c */
|
||||
enum { BATTERY_STATUS, BATTERY_TIME };
|
||||
|
||||
namespace conky::info {
|
||||
|
||||
// Don't guard enum values with #ifdef *_BUILD features; it will only make code
|
||||
// harder to maintain - size_t won't change in size.
|
||||
|
||||
enum class display_session : std::size_t { unknown, x11, wayland };
|
||||
enum class window_manager : std::size_t {
|
||||
unknown,
|
||||
|
||||
// X11
|
||||
awesome,
|
||||
bspwm,
|
||||
compiz,
|
||||
dde, // Deepin
|
||||
dwm,
|
||||
enlightenment,
|
||||
fluxbox,
|
||||
herbstluftwm,
|
||||
i3,
|
||||
kwin,
|
||||
marco,
|
||||
metacity,
|
||||
mutter,
|
||||
openbox,
|
||||
qtile,
|
||||
xfwm,
|
||||
windowmaker,
|
||||
|
||||
// Wayland (only)
|
||||
hyprland,
|
||||
river,
|
||||
sway,
|
||||
wayfire,
|
||||
|
||||
// Remember to update get_system_details when adding new ones!
|
||||
};
|
||||
|
||||
struct system {
|
||||
display_session session;
|
||||
|
||||
window_manager wm;
|
||||
const char *wm_name;
|
||||
};
|
||||
|
||||
} // namespace conky::info
|
||||
|
||||
struct information {
|
||||
unsigned int mask;
|
||||
|
||||
@@ -229,6 +276,11 @@ struct information {
|
||||
csr_config_t csr_config;
|
||||
csr_config_flags_t csr_config_flags;
|
||||
#endif /* defined(__APPLE__) && defined(__MACH__) */
|
||||
|
||||
/**
|
||||
* @brief General information about the system currently running conky.
|
||||
*/
|
||||
conky::info::system system;
|
||||
};
|
||||
|
||||
class music_player_interval_setting
|
||||
@@ -320,6 +372,20 @@ int spaced_print(char *, int, const char *, int, ...)
|
||||
__attribute__((format(printf, 3, 5)));
|
||||
extern int inotify_fd;
|
||||
|
||||
template <
|
||||
typename Iterable = std::initializer_list<conky::info::window_manager>>
|
||||
inline bool wm_is(const Iterable &values) {
|
||||
// if constexpr (!ENABLE_RUNTIME_TWEAKS) { return false; }
|
||||
// can't assume unknown isn't in the list...
|
||||
for (const auto &wm : values) {
|
||||
if (info.system.wm == wm) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool wm_is(conky::info::window_manager single) {
|
||||
return info.system.wm == single;
|
||||
}
|
||||
|
||||
/* defined in conky.c
|
||||
* evaluates 'text' and places the result in 'p' of max length 'p_max_size'
|
||||
*/
|
||||
|
@@ -29,6 +29,7 @@
|
||||
*/
|
||||
|
||||
#include "top.h"
|
||||
|
||||
#include "../logging.h"
|
||||
#include "../prioqueue.h"
|
||||
|
||||
@@ -117,21 +118,24 @@ void free_all_processes() {
|
||||
unhash_all_processes();
|
||||
}
|
||||
|
||||
struct process *get_process_by_name(const char *name) {
|
||||
struct process *get_process_by_name(std::string_view name) {
|
||||
struct process *p = first_process;
|
||||
|
||||
while (p != nullptr) {
|
||||
/* Try matching against the full command line first. If that fails,
|
||||
* fall back to the basename.
|
||||
*/
|
||||
if (((p->name != nullptr) && (strcmp(p->name, name) == 0)) ||
|
||||
((p->basename != nullptr) && (strcmp(p->basename, name) == 0))) {
|
||||
// Try matching against the full command line first.
|
||||
if (p->name != nullptr && name == p->name) { return p; }
|
||||
// If matching against full command line fails, fall back to the basename.
|
||||
if (p->basename != nullptr && name == p->basename) {
|
||||
return p;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
bool is_process_running(std::string_view name) {
|
||||
return get_process_by_name(name) != nullptr;
|
||||
}
|
||||
|
||||
static struct process *find_process(pid_t pid) {
|
||||
struct proc_hash_entry *phe;
|
||||
|
@@ -42,7 +42,11 @@
|
||||
|
||||
#include "../conky.h"
|
||||
#include "../content/text_object.h"
|
||||
|
||||
#define CPU_THRESHHOLD 0 /* threshold for the cpu diff to appear */
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
@@ -116,8 +120,21 @@ struct sorted_process {
|
||||
struct process *proc;
|
||||
};
|
||||
|
||||
/* lookup a program by it's name */
|
||||
struct process *get_process_by_name(const char *);
|
||||
/**
|
||||
* @brief Returns process information for specified `name`.
|
||||
*
|
||||
* @param name full command line or base name of the process.
|
||||
* @return struct process* containing usage details of a process.
|
||||
*/
|
||||
struct process *get_process_by_name(std::string_view name);
|
||||
/**
|
||||
* @brief Checks if a process is running.
|
||||
*
|
||||
* @param name full command line or base name of the process.
|
||||
* @return `true` if process with given command line or base name is currently
|
||||
* running, `false` otherwise.
|
||||
*/
|
||||
bool is_process_running(std::string_view name);
|
||||
|
||||
int parse_top_args(const char *s, const char *arg, struct text_object *obj);
|
||||
|
||||
|
@@ -333,9 +333,10 @@ bool display_output_x11::main_loop_wait(double t) {
|
||||
}
|
||||
|
||||
/* update struts */
|
||||
if ((changed != 0) && own_window_type.get(*state) == window_type::PANEL) {
|
||||
NORM_ERR("defining struts");
|
||||
set_struts(text_alignment.get(*state));
|
||||
if (changed != 0) {
|
||||
auto window_type = own_window_type.get(*state);
|
||||
// Openbox will implicitly set struts even for window_type::DOCK
|
||||
if (window_type == window_type::PANEL) { set_struts(); }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -167,7 +167,6 @@ extern char window_created;
|
||||
|
||||
void destroy_window(void);
|
||||
void create_gc(void);
|
||||
void set_struts(int);
|
||||
|
||||
bool out_to_gui(lua::state &l);
|
||||
|
||||
|
@@ -41,8 +41,8 @@
|
||||
#include "../common.h"
|
||||
#include "../conky.h"
|
||||
#include "../geometry.h"
|
||||
#include "gui.h"
|
||||
#include "../logging.h"
|
||||
#include "gui.h"
|
||||
|
||||
#ifdef BUILD_XINPUT
|
||||
#include "../mouse-events.h"
|
||||
@@ -1119,7 +1119,8 @@ void print_desktop_name(struct text_object *obj, char *p,
|
||||
}
|
||||
|
||||
#ifdef OWN_WINDOW
|
||||
enum class x11_strut : size_t {
|
||||
namespace x11_strut {
|
||||
enum value : size_t {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
TOP,
|
||||
@@ -1132,66 +1133,255 @@ enum class x11_strut : size_t {
|
||||
TOP_END_X,
|
||||
BOTTOM_START_X,
|
||||
BOTTOM_END_X,
|
||||
COUNT,
|
||||
};
|
||||
const size_t STRUT_COUNT = static_cast<size_t>(x11_strut::BOTTOM_END_X) + 1;
|
||||
constexpr size_t operator*(x11_strut index) {
|
||||
return static_cast<size_t>(index);
|
||||
|
||||
inline std::array<long, x11_strut::COUNT> array() {
|
||||
std::array<long, COUNT> sizes;
|
||||
std::memset(sizes.data(), 0, sizeof(long) * COUNT);
|
||||
return sizes;
|
||||
}
|
||||
} // namespace x11_strut
|
||||
|
||||
/* reserve window manager space */
|
||||
void set_struts(alignment align) {
|
||||
// Middle and none align don't have least significant bit set.
|
||||
// Ensures either vertical or horizontal axis are start/end
|
||||
if ((*align & 0b0101) == 0) return;
|
||||
void set_struts() {
|
||||
/* clang-format on */
|
||||
static bool warn_once = true;
|
||||
if (warn_once) {
|
||||
// Before adding new sessions to the unsupported list, please check whether
|
||||
// it's at all possible to support them by re-arranging values provided in
|
||||
// this function
|
||||
/* clang-format off */
|
||||
const bool unsupported = wm_is({
|
||||
/* clang-format off */
|
||||
conky::info::window_manager::enlightenment // has its own gadgets system; requires a custom output and additional libraries
|
||||
/* clang-format on */
|
||||
});
|
||||
|
||||
Atom strut = ATOM(_NET_WM_STRUT);
|
||||
if (strut != None) {
|
||||
long sizes[STRUT_COUNT] = {0};
|
||||
|
||||
int display_width = workarea.width();
|
||||
int display_height = workarea.height();
|
||||
|
||||
switch (align) {
|
||||
case alignment::TOP_LEFT:
|
||||
case alignment::TOP_RIGHT:
|
||||
case alignment::TOP_MIDDLE:
|
||||
sizes[*x11_strut::TOP] = std::clamp(window.geometry.end_y(), 0, display_height);
|
||||
sizes[*x11_strut::TOP_START_X] = std::clamp(window.geometry.x(), 0, display_width);
|
||||
sizes[*x11_strut::TOP_END_X] = std::clamp(window.geometry.end_x(), 0, display_width);
|
||||
break;
|
||||
case alignment::BOTTOM_LEFT:
|
||||
case alignment::BOTTOM_RIGHT:
|
||||
case alignment::BOTTOM_MIDDLE:
|
||||
sizes[*x11_strut::BOTTOM] = display_height - std::clamp(window.geometry.y(), 0, display_height);
|
||||
sizes[*x11_strut::BOTTOM_START_X] = std::clamp(window.geometry.x(), 0, display_width);
|
||||
sizes[*x11_strut::BOTTOM_END_X] = std::clamp(window.geometry.end_x(), 0, display_width);
|
||||
break;
|
||||
case alignment::MIDDLE_LEFT:
|
||||
sizes[*x11_strut::LEFT] = std::clamp(window.geometry.end_x(), 0, display_width);
|
||||
sizes[*x11_strut::LEFT_START_Y] = std::clamp(window.geometry.y(), 0, display_height);
|
||||
sizes[*x11_strut::LEFT_END_Y] = std::clamp(window.geometry.end_y(), 0, display_height);
|
||||
break;
|
||||
case alignment::MIDDLE_RIGHT:
|
||||
sizes[*x11_strut::RIGHT] = display_width - std::clamp(window.geometry.x(), 0, display_width);
|
||||
sizes[*x11_strut::RIGHT_START_Y] = std::clamp(window.geometry.y(), 0, display_height);
|
||||
sizes[*x11_strut::RIGHT_END_Y] = std::clamp(window.geometry.end_y(), 0, display_height);
|
||||
break;
|
||||
default:
|
||||
// can't reserve space in middle of the screen
|
||||
break;
|
||||
if (unsupported) {
|
||||
// feel free to add any special support
|
||||
NORM_ERR(
|
||||
"WM/DE you're using (%s) doesn't support WM_STRUT hints (well); "
|
||||
"reserved area functionality might not work correctly",
|
||||
info.system.wm_name);
|
||||
}
|
||||
warn_once = false;
|
||||
}
|
||||
|
||||
XChangeProperty(display, window.window, strut, XA_CARDINAL, 32,
|
||||
PropModeReplace, reinterpret_cast<unsigned char *>(&sizes),
|
||||
4);
|
||||
// Most WMs simply subtract the primary strut side from workarea where windows
|
||||
// will be placed. e.g. TOP_LEFT will cause all windows to be shifted down
|
||||
// even if conky is a thin and tall window. It's our responsibility to set the
|
||||
// primary strut side to the value that's going to eat up least available
|
||||
// space.
|
||||
//
|
||||
// compiz:
|
||||
// https://github.com/compiz-reloaded/compiz/blob/155c201ec62c289c5a44b6dd23eddec3e3c20e26/src/window.c#L1843
|
||||
// Fluxbox:
|
||||
// https://github.com/fluxbox/fluxbox/blob/88bbf811dade299ca2dac66cb81e4b3d96cfe741/src/Ewmh.cc#L1397
|
||||
// i3:
|
||||
// https://github.com/i3/i3/blob/cfa4cf16bea809c7c715a86c428757e577c85254/src/manage.c#L260
|
||||
// Mutter:
|
||||
// https://gitlab.gnome.org/GNOME/mutter/-/blob/ea8b65d0c92ebf84060ea0b5c61d02fa9004e1e9/src/core/boxes.c#L564
|
||||
// Openbox:
|
||||
// https://github.com/Mikachu/openbox/blob/dac6e2f6f8f2e0c5586a9e19f18508a03db639cb/openbox/screen.c#L1428
|
||||
// xfwm:
|
||||
// https://github.com/xfce-mirror/xfwm4/blob/37636f55bcbca064e62489d7fe183ef7b9371b4c/src/placement.c#L220
|
||||
//
|
||||
// The EWMH spec doesn't handle placement of panels/docks in middle of the
|
||||
// screen (e.g. left side of the right monitor with Xinerama). Submissions are
|
||||
// closed and it won't be fixed.
|
||||
// See: https://gitlab.freedesktop.org/xdg/xdg-specs/-/merge_requests/22
|
||||
|
||||
strut = ATOM(_NET_WM_STRUT_PARTIAL);
|
||||
if (strut != None) {
|
||||
XChangeProperty(display, window.window, strut, XA_CARDINAL, 32,
|
||||
PropModeReplace,
|
||||
reinterpret_cast<unsigned char *>(&sizes), 12);
|
||||
Atom atom = ATOM(_NET_WM_STRUT);
|
||||
if (atom == None) return;
|
||||
|
||||
auto sizes = x11_strut::array();
|
||||
|
||||
const int display_width = DisplayWidth(display, screen);
|
||||
const int display_height = DisplayHeight(display, screen);
|
||||
|
||||
bool supports_cutout =
|
||||
ENABLE_RUNTIME_TWEAKS &&
|
||||
wm_is({
|
||||
/* clang-format off */
|
||||
conky::info::window_manager::compiz,
|
||||
conky::info::window_manager::fluxbox,
|
||||
conky::info::window_manager::i3, // only uses WM_STRUT_PARTIAL to determine top/bottom dock placement
|
||||
conky::info::window_manager::kwin,
|
||||
/* clang-format on */
|
||||
});
|
||||
|
||||
if (supports_cutout) {
|
||||
alignment align = text_alignment.get(*state);
|
||||
// Middle and none align don't have least significant bit set.
|
||||
// Ensures either vertical or horizontal axis are start/end
|
||||
if ((*align & 0b0101) == 0) return;
|
||||
|
||||
// Compute larger dimension only once; so we don't jump between axes for
|
||||
// corner alignments.
|
||||
// If window is wider than it's tall, top/bottom placement is preferred.
|
||||
// It's also preferred for WMs that completely ignore horizontal docks.
|
||||
static bool is_wide_window =
|
||||
window.geometry.width() > window.geometry.height() ||
|
||||
wm_is(conky::info::window_manager::i3);
|
||||
if (is_wide_window) {
|
||||
switch (align) {
|
||||
case alignment::TOP_LEFT:
|
||||
case alignment::TOP_RIGHT:
|
||||
case alignment::TOP_MIDDLE:
|
||||
sizes[x11_strut::TOP] =
|
||||
std::clamp(window.geometry.end_y(), 0, display_height);
|
||||
sizes[x11_strut::TOP_START_X] =
|
||||
std::clamp(window.geometry.x(), 0, display_width);
|
||||
sizes[x11_strut::TOP_END_X] =
|
||||
std::clamp(window.geometry.end_x(), 0, display_width);
|
||||
break;
|
||||
case alignment::BOTTOM_LEFT:
|
||||
case alignment::BOTTOM_RIGHT:
|
||||
case alignment::BOTTOM_MIDDLE:
|
||||
sizes[x11_strut::BOTTOM] =
|
||||
display_height -
|
||||
std::clamp(window.geometry.y(), 0, display_height);
|
||||
sizes[x11_strut::BOTTOM_START_X] =
|
||||
std::clamp(window.geometry.x(), 0, display_width);
|
||||
sizes[x11_strut::BOTTOM_END_X] =
|
||||
std::clamp(window.geometry.end_x(), 0, display_width);
|
||||
break;
|
||||
case alignment::MIDDLE_LEFT:
|
||||
sizes[x11_strut::LEFT] =
|
||||
std::clamp(window.geometry.end_x(), 0, display_width);
|
||||
sizes[x11_strut::LEFT_START_Y] =
|
||||
std::clamp(window.geometry.y(), 0, display_height);
|
||||
sizes[x11_strut::LEFT_END_Y] =
|
||||
std::clamp(window.geometry.end_y(), 0, display_height);
|
||||
break;
|
||||
case alignment::MIDDLE_RIGHT:
|
||||
sizes[x11_strut::RIGHT] =
|
||||
display_width - std::clamp(window.geometry.x(), 0, display_width);
|
||||
sizes[x11_strut::RIGHT_START_Y] =
|
||||
std::clamp(window.geometry.y(), 0, display_height);
|
||||
sizes[x11_strut::RIGHT_END_Y] =
|
||||
std::clamp(window.geometry.end_y(), 0, display_height);
|
||||
break;
|
||||
default:
|
||||
// can't reserve space in middle of the screen
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// if window is thin, prefer left/right placement
|
||||
switch (align) {
|
||||
case alignment::TOP_LEFT:
|
||||
case alignment::MIDDLE_LEFT:
|
||||
case alignment::BOTTOM_LEFT:
|
||||
sizes[x11_strut::LEFT] =
|
||||
std::clamp(window.geometry.end_x(), 0, display_width);
|
||||
sizes[x11_strut::LEFT_START_Y] =
|
||||
std::clamp(window.geometry.y(), 0, display_height);
|
||||
sizes[x11_strut::LEFT_END_Y] =
|
||||
std::clamp(window.geometry.end_y(), 0, display_height);
|
||||
break;
|
||||
case alignment::TOP_RIGHT:
|
||||
case alignment::MIDDLE_RIGHT:
|
||||
case alignment::BOTTOM_RIGHT:
|
||||
sizes[x11_strut::RIGHT] =
|
||||
display_width - std::clamp(window.geometry.x(), 0, display_width);
|
||||
sizes[x11_strut::RIGHT_START_Y] =
|
||||
std::clamp(window.geometry.y(), 0, display_height);
|
||||
sizes[x11_strut::RIGHT_END_Y] =
|
||||
std::clamp(window.geometry.end_y(), 0, display_height);
|
||||
break;
|
||||
case alignment::TOP_MIDDLE:
|
||||
sizes[x11_strut::TOP] =
|
||||
std::clamp(window.geometry.end_y(), 0, display_height);
|
||||
sizes[x11_strut::TOP_START_X] =
|
||||
std::clamp(window.geometry.x(), 0, display_width);
|
||||
sizes[x11_strut::TOP_END_X] =
|
||||
std::clamp(window.geometry.end_x(), 0, display_width);
|
||||
break;
|
||||
case alignment::BOTTOM_MIDDLE:
|
||||
sizes[x11_strut::BOTTOM] =
|
||||
display_height -
|
||||
std::clamp(window.geometry.y(), 0, display_height);
|
||||
sizes[x11_strut::BOTTOM_START_X] =
|
||||
std::clamp(window.geometry.x(), 0, display_width);
|
||||
sizes[x11_strut::BOTTOM_END_X] =
|
||||
std::clamp(window.geometry.end_x(), 0, display_width);
|
||||
break;
|
||||
default:
|
||||
// can't reserve space in middle of the screen
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This approach works better for fully spec-compliant WMs
|
||||
if (window.geometry.width() < window.geometry.height()) {
|
||||
const int space_left = window.geometry.end_x();
|
||||
const int space_right =
|
||||
display_width - window.geometry.end_x() + window.geometry.width();
|
||||
if (space_left < space_right) {
|
||||
sizes[x11_strut::LEFT] =
|
||||
std::clamp(window.geometry.end_x(), 0, display_width);
|
||||
sizes[x11_strut::LEFT_START_Y] =
|
||||
std::clamp(window.geometry.y(), 0, display_height);
|
||||
sizes[x11_strut::LEFT_END_Y] =
|
||||
std::clamp(window.geometry.end_y(), 0, display_height);
|
||||
} else {
|
||||
// we subtract x from display_width in case conky isn't flush with the
|
||||
// right screen side; i.e. there's a gap between conky and the right
|
||||
// side of the screen
|
||||
sizes[x11_strut::RIGHT] =
|
||||
display_width - std::clamp(window.geometry.x(), 0, display_width);
|
||||
sizes[x11_strut::RIGHT_START_Y] =
|
||||
std::clamp(window.geometry.y(), 0, display_height);
|
||||
sizes[x11_strut::RIGHT_END_Y] =
|
||||
std::clamp(window.geometry.end_y(), 0, display_height);
|
||||
}
|
||||
} else {
|
||||
const int space_top = window.geometry.end_y();
|
||||
const int space_bottom =
|
||||
display_height - window.geometry.end_y() + window.geometry.height();
|
||||
if (space_top < space_bottom) {
|
||||
sizes[x11_strut::TOP] =
|
||||
std::clamp(window.geometry.end_y(), 0, display_height);
|
||||
sizes[x11_strut::TOP_START_X] =
|
||||
std::clamp(window.geometry.x(), 0, display_width);
|
||||
sizes[x11_strut::TOP_END_X] =
|
||||
std::clamp(window.geometry.end_x(), 0, display_width);
|
||||
} else {
|
||||
// we subtract y from display_height in case conky isn't flush with the
|
||||
// bottom screen side; i.e. there's a gap between conky and the bottom
|
||||
// of the screen
|
||||
sizes[x11_strut::BOTTOM] =
|
||||
display_height - std::clamp(window.geometry.y(), 0, display_height);
|
||||
sizes[x11_strut::BOTTOM_START_X] =
|
||||
std::clamp(window.geometry.x(), 0, display_width);
|
||||
sizes[x11_strut::BOTTOM_END_X] =
|
||||
std::clamp(window.geometry.end_x(), 0, display_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DBGP(
|
||||
"Reserved space: left=%d, right=%d, top=%d, "
|
||||
"bottom=%d",
|
||||
sizes[0], sizes[1], sizes[2], sizes[3]);
|
||||
|
||||
XChangeProperty(display, window.window, atom, XA_CARDINAL, 32,
|
||||
PropModeReplace, reinterpret_cast<unsigned char *>(&sizes),
|
||||
4);
|
||||
|
||||
atom = ATOM(_NET_WM_STRUT_PARTIAL);
|
||||
if (atom == None) return;
|
||||
|
||||
DBGP(
|
||||
"Reserved space edges: left_start_y=%d, left_end_y=%d, "
|
||||
"right_start_y=%d, right_end_y=%d, top_start_x=%d, "
|
||||
"top_end_x=%d, bottom_start_x=%d, bottom_end_x=%d",
|
||||
sizes[4], sizes[5], sizes[6], sizes[7], sizes[8], sizes[9], sizes[10],
|
||||
sizes[11]);
|
||||
|
||||
XChangeProperty(display, window.window, atom, XA_CARDINAL, 32,
|
||||
PropModeReplace, reinterpret_cast<unsigned char *>(&sizes),
|
||||
12);
|
||||
}
|
||||
#endif /* OWN_WINDOW */
|
||||
|
||||
|
@@ -105,7 +105,12 @@ 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(alignment alignment);
|
||||
/// @brief Sets reserved area atoms for the conky window to avoid other windows
|
||||
/// covering it.
|
||||
///
|
||||
/// Prints out a warning if user is using one of sessions that are known not to
|
||||
/// work.
|
||||
void set_struts();
|
||||
void x11_init_window(lua::state &l, bool own);
|
||||
void deinit_x11();
|
||||
|
||||
|
Reference in New Issue
Block a user