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:
Tin Švagelj
2025-05-04 23:09:19 +02:00
committed by GitHub
parent ca8874e21c
commit 5125cbc2f5
13 changed files with 485 additions and 86 deletions

View File

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

View File

@@ -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)

View File

@@ -50,6 +50,8 @@
#cmakedefine OWN_WINDOW 1
#cmakedefine ENABLE_RUNTIME_TWEAKS 1
#cmakedefine BUILD_MOUSE_EVENTS 1
#cmakedefine BUILD_XDAMAGE 1

View File

@@ -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.

View File

@@ -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;

View File

@@ -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());

View File

@@ -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'
*/

View File

@@ -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;

View File

@@ -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);

View File

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

View File

@@ -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);

View File

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

View File

@@ -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();