2018-10-03 21:14:51 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// Copyright (c) 2011-2013, Christopher Jeffrey
|
|
|
|
// Copyright (c) 2013 Richard Grenville <pyxlcy@gmail.com>
|
|
|
|
|
2019-01-20 16:53:39 +00:00
|
|
|
#include <X11/Xlib.h>
|
2019-01-20 21:15:20 +00:00
|
|
|
#include <X11/Xutil.h>
|
2020-10-28 19:02:57 +00:00
|
|
|
#include <inttypes.h>
|
2018-09-06 18:17:26 +00:00
|
|
|
#include <math.h>
|
2019-03-10 12:34:37 +00:00
|
|
|
#include <stdbool.h>
|
2019-01-20 21:15:20 +00:00
|
|
|
#include <stdlib.h>
|
2019-03-10 12:34:37 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <xcb/composite.h>
|
|
|
|
#include <xcb/damage.h>
|
|
|
|
#include <xcb/render.h>
|
|
|
|
#include <xcb/xcb.h>
|
|
|
|
#include <xcb/xcb_renderutil.h>
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-05-05 23:34:08 +00:00
|
|
|
#include "atom.h"
|
2019-03-10 12:34:37 +00:00
|
|
|
#include "backend/backend.h"
|
|
|
|
#include "c2.h"
|
2019-05-24 22:29:26 +00:00
|
|
|
#include "common.h"
|
2019-03-10 12:34:37 +00:00
|
|
|
#include "compiler.h"
|
|
|
|
#include "config.h"
|
2024-03-23 13:48:40 +00:00
|
|
|
#include "dbus.h"
|
2019-04-17 22:14:45 +00:00
|
|
|
#include "list.h"
|
2018-12-15 17:53:17 +00:00
|
|
|
#include "log.h"
|
2020-02-07 21:35:34 +00:00
|
|
|
#include "picom.h"
|
2019-01-20 16:53:39 +00:00
|
|
|
#include "region.h"
|
|
|
|
#include "render.h"
|
2019-03-10 12:34:37 +00:00
|
|
|
#include "string_utils.h"
|
2024-03-19 16:44:02 +00:00
|
|
|
#include "transition.h"
|
2019-03-10 12:34:37 +00:00
|
|
|
#include "types.h"
|
2019-04-03 07:36:02 +00:00
|
|
|
#include "uthash_extra.h"
|
2019-03-10 12:34:37 +00:00
|
|
|
#include "utils.h"
|
2024-03-22 02:41:48 +00:00
|
|
|
#include "win_defs.h"
|
2019-03-10 12:34:37 +00:00
|
|
|
#include "x.h"
|
2024-03-24 02:02:14 +00:00
|
|
|
#include "xcb/xproto.h"
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-02-09 15:50:40 +00:00
|
|
|
#ifdef CONFIG_OPENGL
|
2020-08-30 14:25:58 +00:00
|
|
|
// TODO(yshui) Get rid of this include
|
2019-02-09 15:50:40 +00:00
|
|
|
#include "opengl.h"
|
|
|
|
#endif
|
|
|
|
|
2018-09-06 18:17:26 +00:00
|
|
|
#include "win.h"
|
|
|
|
|
2020-08-30 14:25:58 +00:00
|
|
|
// TODO(yshui) Make more window states internal
|
2019-09-20 00:54:14 +00:00
|
|
|
struct managed_win_internal {
|
|
|
|
struct managed_win base;
|
|
|
|
};
|
|
|
|
|
2019-05-23 23:06:53 +00:00
|
|
|
#define OPAQUE (0xffffffff)
|
2019-05-06 01:03:07 +00:00
|
|
|
static const int WIN_GET_LEADER_MAX_RECURSION = 20;
|
|
|
|
static const int ROUNDED_PIXELS = 1;
|
|
|
|
static const double ROUNDED_PERCENT = 0.05;
|
2019-02-09 15:50:40 +00:00
|
|
|
|
2020-10-22 14:48:11 +00:00
|
|
|
/**
|
|
|
|
* Reread opacity property of a window.
|
|
|
|
*/
|
2024-02-19 22:09:01 +00:00
|
|
|
static void win_update_opacity_prop(struct x_connection *c, struct atom *atoms,
|
|
|
|
struct managed_win *w, bool detect_client_opacity);
|
2020-10-22 14:48:11 +00:00
|
|
|
static void win_update_opacity_target(session_t *ps, struct managed_win *w);
|
2024-02-19 17:17:35 +00:00
|
|
|
static void win_update_prop_shadow_raw(struct x_connection *c, struct atom *atoms,
|
|
|
|
struct managed_win *w);
|
2024-02-19 17:45:34 +00:00
|
|
|
static bool
|
|
|
|
win_update_prop_shadow(struct x_connection *c, struct atom *atoms, struct managed_win *w);
|
2020-10-22 14:48:11 +00:00
|
|
|
/**
|
|
|
|
* Update leader of a window.
|
|
|
|
*/
|
2024-02-19 18:11:42 +00:00
|
|
|
static xcb_window_t
|
|
|
|
win_get_leader_property(struct x_connection *c, struct atom *atoms, xcb_window_t wid,
|
|
|
|
bool detect_transient, bool detect_client_leader);
|
2024-02-19 22:56:07 +00:00
|
|
|
static void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client);
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2020-09-05 14:43:25 +00:00
|
|
|
/// Generate a "no corners" region function, from a function that returns the
|
|
|
|
/// region via a region_t pointer argument. Corners of the window will be removed from
|
|
|
|
/// the returned region.
|
|
|
|
/// Function signature has to be (win *, region_t *)
|
|
|
|
#define gen_without_corners(fun) \
|
|
|
|
void fun##_without_corners(const struct managed_win *w, region_t *res) { \
|
|
|
|
fun(w, res); \
|
|
|
|
win_region_remove_corners(w, res); \
|
|
|
|
}
|
|
|
|
|
2018-10-13 00:17:59 +00:00
|
|
|
/// Generate a "return by value" function, from a function that returns the
|
|
|
|
/// region via a region_t pointer argument.
|
2020-09-05 14:43:25 +00:00
|
|
|
/// Function signature has to be (win *)
|
2019-03-10 12:34:37 +00:00
|
|
|
#define gen_by_val(fun) \
|
2019-04-17 22:14:45 +00:00
|
|
|
region_t fun##_by_val(const struct managed_win *w) { \
|
2019-03-10 12:34:37 +00:00
|
|
|
region_t ret; \
|
|
|
|
pixman_region32_init(&ret); \
|
|
|
|
fun(w, &ret); \
|
|
|
|
return ret; \
|
|
|
|
}
|
2018-10-13 00:17:59 +00:00
|
|
|
|
2019-08-10 00:44:27 +00:00
|
|
|
static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int recursions);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the leader of a window.
|
|
|
|
*
|
|
|
|
* This function updates w->cache_leader if necessary.
|
|
|
|
*/
|
|
|
|
static inline xcb_window_t win_get_leader(session_t *ps, struct managed_win *w) {
|
|
|
|
return win_get_leader_raw(ps, w, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update focused state of a window.
|
|
|
|
*/
|
|
|
|
static void win_update_focused(session_t *ps, struct managed_win *w) {
|
2024-03-24 02:02:14 +00:00
|
|
|
auto c2_state = session_get_c2(ps);
|
2024-02-15 00:07:31 +00:00
|
|
|
if (w->focused_force != UNSET) {
|
2019-08-10 00:44:27 +00:00
|
|
|
w->focused = w->focused_force;
|
|
|
|
} else {
|
2024-02-19 22:47:28 +00:00
|
|
|
bool is_wmwin = win_is_wmwin(w);
|
2024-03-24 02:02:14 +00:00
|
|
|
auto options = session_get_options(ps);
|
2024-02-15 00:07:31 +00:00
|
|
|
w->focused = win_is_focused_raw(w);
|
2019-08-10 00:44:27 +00:00
|
|
|
|
|
|
|
// Use wintype_focus, and treat WM windows and override-redirected
|
|
|
|
// windows specially
|
2024-03-24 02:02:14 +00:00
|
|
|
if (options->wintype_option[w->window_type].focus ||
|
|
|
|
(options->mark_wmwin_focused && is_wmwin) ||
|
|
|
|
(options->mark_ovredir_focused && w->base.id == w->client_win && !is_wmwin) ||
|
2019-08-10 00:44:27 +00:00
|
|
|
(w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
|
2024-03-24 02:02:14 +00:00
|
|
|
c2_match(c2_state, w, options->focus_blacklist, NULL))) {
|
2019-08-10 00:44:27 +00:00
|
|
|
w->focused = true;
|
2020-11-27 05:34:54 +00:00
|
|
|
}
|
2019-08-10 00:44:27 +00:00
|
|
|
|
|
|
|
// If window grouping detection is enabled, mark the window active if
|
|
|
|
// its group is
|
2024-03-24 02:02:14 +00:00
|
|
|
auto active_leader = session_get_active_leader(ps);
|
|
|
|
if (options->track_leader && active_leader &&
|
|
|
|
win_get_leader(ps, w) == active_leader) {
|
2019-08-10 00:44:27 +00:00
|
|
|
w->focused = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
static inline void group_on_factor_change_callback(struct win *w, void *user_data) {
|
|
|
|
struct {
|
|
|
|
session_t *ps;
|
|
|
|
xcb_window_t leader;
|
|
|
|
} *data = user_data;
|
|
|
|
if (!w->managed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto mw = (struct managed_win *)w;
|
|
|
|
if (win_get_leader(data->ps, mw) == data->leader) {
|
|
|
|
win_on_factor_change(data->ps, mw);
|
|
|
|
}
|
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
/**
|
2020-03-17 19:50:48 +00:00
|
|
|
* Run win_on_factor_change() on all windows with the same leader window.
|
2018-09-06 18:17:26 +00:00
|
|
|
*
|
|
|
|
* @param leader leader window ID
|
|
|
|
*/
|
2020-03-17 19:50:48 +00:00
|
|
|
static inline void group_on_factor_change(session_t *ps, xcb_window_t leader) {
|
2020-12-03 23:53:10 +00:00
|
|
|
if (!leader) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
2020-12-03 23:53:10 +00:00
|
|
|
}
|
2024-03-24 02:02:14 +00:00
|
|
|
struct {
|
|
|
|
session_t *ps;
|
|
|
|
xcb_window_t leader;
|
|
|
|
} data = {ps, leader};
|
|
|
|
session_foreach_win(ps, group_on_factor_change_callback, &data);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
static inline void group_is_focused_callback(struct win *w, void *user_data) {
|
|
|
|
struct {
|
|
|
|
session_t *ps;
|
|
|
|
xcb_window_t leader;
|
|
|
|
bool is_focused;
|
|
|
|
} *data = user_data;
|
2019-08-10 00:44:27 +00:00
|
|
|
if (!w->managed) {
|
2024-03-24 02:02:14 +00:00
|
|
|
return;
|
2019-08-10 00:44:27 +00:00
|
|
|
}
|
|
|
|
auto mw = (struct managed_win *)w;
|
2024-03-24 02:02:14 +00:00
|
|
|
if (!data->is_focused && win_get_leader(data->ps, mw) == data->leader &&
|
|
|
|
win_is_focused_raw(mw)) {
|
|
|
|
data->is_focused = true;
|
|
|
|
}
|
2019-08-10 00:44:27 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 18:17:26 +00:00
|
|
|
/**
|
|
|
|
* Return whether a window group is really focused.
|
|
|
|
*
|
|
|
|
* @param leader leader window ID
|
|
|
|
* @return true if the window group is focused, false otherwise
|
|
|
|
*/
|
2019-03-10 12:34:37 +00:00
|
|
|
static inline bool group_is_focused(session_t *ps, xcb_window_t leader) {
|
2019-04-17 22:14:45 +00:00
|
|
|
if (!leader) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return false;
|
2019-04-17 22:14:45 +00:00
|
|
|
}
|
2024-03-24 02:02:14 +00:00
|
|
|
struct {
|
|
|
|
session_t *ps;
|
|
|
|
xcb_window_t leader;
|
|
|
|
bool is_focused;
|
|
|
|
} data = {ps, leader, false};
|
|
|
|
session_foreach_win(ps, group_is_focused_callback, &data);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
return data.is_focused;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2024-02-19 18:11:42 +00:00
|
|
|
/**
|
|
|
|
* Set leader of a window.
|
|
|
|
*/
|
|
|
|
static inline void win_set_leader(session_t *ps, struct managed_win *w, xcb_window_t nleader) {
|
|
|
|
xcb_window_t cache_leader_old = win_get_leader(ps, w);
|
|
|
|
|
|
|
|
w->leader = nleader;
|
|
|
|
|
|
|
|
// Forcefully do this to deal with the case when a child window
|
|
|
|
// gets mapped before parent, or when the window is a waypoint
|
2024-03-24 02:02:14 +00:00
|
|
|
session_clear_cache_win_leaders(ps);
|
2024-02-19 18:11:42 +00:00
|
|
|
|
|
|
|
// Update the old and new window group and active_leader if the
|
|
|
|
// window could affect their state.
|
|
|
|
xcb_window_t cache_leader = win_get_leader(ps, w);
|
|
|
|
if (win_is_focused_raw(w) && cache_leader_old != cache_leader) {
|
2024-03-24 02:02:14 +00:00
|
|
|
session_set_active_leader(ps, cache_leader);
|
2024-02-19 18:11:42 +00:00
|
|
|
group_on_factor_change(ps, cache_leader_old);
|
|
|
|
group_on_factor_change(ps, cache_leader);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-06 18:17:26 +00:00
|
|
|
/**
|
|
|
|
* Get a rectangular region a window occupies, excluding shadow.
|
|
|
|
*/
|
2019-04-17 22:14:45 +00:00
|
|
|
static void win_get_region_local(const struct managed_win *w, region_t *res) {
|
2019-03-30 09:07:21 +00:00
|
|
|
assert(w->widthb >= 0 && w->heightb >= 0);
|
2019-03-10 12:34:37 +00:00
|
|
|
pixman_region32_fini(res);
|
2019-03-30 09:07:21 +00:00
|
|
|
pixman_region32_init_rect(res, 0, 0, (uint)w->widthb, (uint)w->heightb);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a rectangular region a window occupies, excluding frame and shadow.
|
|
|
|
*/
|
2019-04-17 22:14:45 +00:00
|
|
|
void win_get_region_noframe_local(const struct managed_win *w, region_t *res) {
|
2019-03-10 12:34:37 +00:00
|
|
|
const margin_t extents = win_calc_frame_extents(w);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-30 10:19:31 +00:00
|
|
|
int x = extents.left;
|
|
|
|
int y = extents.top;
|
2021-06-28 18:10:00 +00:00
|
|
|
int width = max2(w->widthb - (extents.left + extents.right), 0);
|
|
|
|
int height = max2(w->heightb - (extents.top + extents.bottom), 0);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
pixman_region32_fini(res);
|
2019-03-30 09:07:21 +00:00
|
|
|
if (width > 0 && height > 0) {
|
|
|
|
pixman_region32_init_rect(res, x, y, (uint)width, (uint)height);
|
2020-09-05 14:43:25 +00:00
|
|
|
} else {
|
|
|
|
pixman_region32_init(res);
|
2019-03-30 09:07:21 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2020-09-05 14:43:25 +00:00
|
|
|
gen_without_corners(win_get_region_noframe_local);
|
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
void win_get_region_frame_local(const struct managed_win *w, region_t *res) {
|
2019-03-10 12:34:37 +00:00
|
|
|
const margin_t extents = win_calc_frame_extents(w);
|
2021-06-28 18:10:00 +00:00
|
|
|
auto outer_width = w->widthb;
|
|
|
|
auto outer_height = w->heightb;
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
pixman_region32_fini(res);
|
|
|
|
pixman_region32_init_rects(
|
|
|
|
res,
|
|
|
|
(rect_t[]){
|
|
|
|
// top
|
2020-03-17 18:32:34 +00:00
|
|
|
{.x1 = 0, .y1 = 0, .x2 = outer_width, .y2 = extents.top},
|
2019-03-10 12:34:37 +00:00
|
|
|
// bottom
|
2020-03-17 18:32:34 +00:00
|
|
|
{.x1 = 0, .y1 = outer_height - extents.bottom, .x2 = outer_width, .y2 = outer_height},
|
2019-03-10 12:34:37 +00:00
|
|
|
// left
|
2020-03-17 18:32:34 +00:00
|
|
|
{.x1 = 0, .y1 = 0, .x2 = extents.left, .y2 = outer_height},
|
2019-03-10 12:34:37 +00:00
|
|
|
// right
|
2020-03-17 18:32:34 +00:00
|
|
|
{.x1 = outer_width - extents.right, .y1 = 0, .x2 = outer_width, .y2 = outer_height},
|
2019-03-10 12:34:37 +00:00
|
|
|
},
|
|
|
|
4);
|
|
|
|
|
|
|
|
// limit the frame region to inside the window
|
|
|
|
region_t reg_win;
|
2020-10-28 19:27:01 +00:00
|
|
|
pixman_region32_init_rects(®_win, (rect_t[]){{0, 0, outer_width, outer_height}}, 1);
|
2019-03-10 12:34:37 +00:00
|
|
|
pixman_region32_intersect(res, ®_win, res);
|
|
|
|
pixman_region32_fini(®_win);
|
2019-03-04 21:37:22 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
gen_by_val(win_get_region_frame_local);
|
2019-03-04 21:37:22 +00:00
|
|
|
|
2018-09-06 18:17:26 +00:00
|
|
|
/**
|
|
|
|
* Add a window to damaged area.
|
|
|
|
*
|
|
|
|
* @param ps current session
|
|
|
|
* @param w struct _win element representing the window
|
|
|
|
*/
|
2019-04-17 22:14:45 +00:00
|
|
|
void add_damage_from_win(session_t *ps, const struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// XXX there was a cached extents region, investigate
|
|
|
|
// if that's better
|
2020-10-23 03:34:05 +00:00
|
|
|
|
|
|
|
// TODO(yshui) use the bounding shape when the window is shaped, otherwise the
|
|
|
|
// damage would be excessive
|
2019-03-10 12:34:37 +00:00
|
|
|
region_t extents;
|
|
|
|
pixman_region32_init(&extents);
|
|
|
|
win_extents(w, &extents);
|
|
|
|
add_damage(ps, &extents);
|
|
|
|
pixman_region32_fini(&extents);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2019-03-16 20:14:54 +00:00
|
|
|
/// Release the images attached to this window
|
2019-09-19 17:48:12 +00:00
|
|
|
static inline void win_release_pixmap(backend_t *base, struct managed_win *w) {
|
|
|
|
log_debug("Releasing pixmap of window %#010x (%s)", w->base.id, w->name);
|
|
|
|
assert(w->win_image);
|
2019-03-16 20:14:54 +00:00
|
|
|
if (w->win_image) {
|
|
|
|
base->ops->release_image(base, w->win_image);
|
|
|
|
w->win_image = NULL;
|
2020-04-06 19:52:32 +00:00
|
|
|
// Bypassing win_set_flags, because `w` might have been destroyed
|
2019-09-19 17:48:12 +00:00
|
|
|
w->flags |= WIN_FLAGS_PIXMAP_NONE;
|
2019-03-16 20:14:54 +00:00
|
|
|
}
|
2019-09-19 17:48:12 +00:00
|
|
|
}
|
|
|
|
static inline void win_release_shadow(backend_t *base, struct managed_win *w) {
|
|
|
|
log_debug("Releasing shadow of window %#010x (%s)", w->base.id, w->name);
|
|
|
|
if (w->shadow_image) {
|
2024-02-10 16:48:37 +00:00
|
|
|
assert(w->shadow);
|
2019-09-19 17:48:12 +00:00
|
|
|
base->ops->release_image(base, w->shadow_image);
|
|
|
|
w->shadow_image = NULL;
|
2019-03-16 20:14:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-24 06:35:49 +00:00
|
|
|
static inline void win_release_mask(backend_t *base, struct managed_win *w) {
|
|
|
|
if (w->mask_image) {
|
|
|
|
base->ops->release_image(base, w->mask_image);
|
|
|
|
w->mask_image = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-19 17:48:12 +00:00
|
|
|
static inline bool win_bind_pixmap(struct backend_base *b, struct managed_win *w) {
|
|
|
|
assert(!w->win_image);
|
|
|
|
auto pixmap = x_new_id(b->c);
|
2019-03-16 20:14:54 +00:00
|
|
|
auto e = xcb_request_check(
|
2023-06-29 04:39:36 +00:00
|
|
|
b->c->c, xcb_composite_name_window_pixmap_checked(b->c->c, w->base.id, pixmap));
|
2019-03-16 20:14:54 +00:00
|
|
|
if (e) {
|
2019-04-18 07:51:17 +00:00
|
|
|
log_error("Failed to get named pixmap for window %#010x(%s)", w->base.id,
|
|
|
|
w->name);
|
2019-03-16 20:14:54 +00:00
|
|
|
free(e);
|
|
|
|
return false;
|
|
|
|
}
|
2019-09-19 17:48:12 +00:00
|
|
|
log_debug("New named pixmap for %#010x (%s) : %#010x", w->base.id, w->name, pixmap);
|
|
|
|
w->win_image =
|
|
|
|
b->ops->bind_pixmap(b, pixmap, x_get_visual_info(b->c, w->a.visual), true);
|
|
|
|
if (!w->win_image) {
|
|
|
|
log_error("Failed to bind pixmap");
|
2020-04-06 19:52:32 +00:00
|
|
|
win_set_flags(w, WIN_FLAGS_IMAGE_ERROR);
|
2019-03-16 20:14:54 +00:00
|
|
|
return false;
|
|
|
|
}
|
2019-09-19 17:48:12 +00:00
|
|
|
|
2020-04-06 19:52:32 +00:00
|
|
|
win_clear_flags(w, WIN_FLAGS_PIXMAP_NONE);
|
2019-03-16 20:14:54 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
bool win_bind_mask(struct backend_base *b, struct managed_win *w) {
|
2022-11-15 18:03:31 +00:00
|
|
|
assert(!w->mask_image);
|
2022-08-25 16:59:19 +00:00
|
|
|
auto reg_bound_local = win_get_bounding_shape_global_by_val(w);
|
|
|
|
pixman_region32_translate(®_bound_local, -w->g.x, -w->g.y);
|
|
|
|
w->mask_image = b->ops->make_mask(
|
2022-11-30 17:00:46 +00:00
|
|
|
b, (geometry_t){.width = w->widthb, .height = w->heightb}, ®_bound_local);
|
2022-10-10 12:55:05 +00:00
|
|
|
pixman_region32_fini(®_bound_local);
|
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
if (!w->mask_image) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
b->ops->set_image_property(b, IMAGE_PROPERTY_CORNER_RADIUS, w->mask_image,
|
|
|
|
(double[]){w->corner_radius});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-09-28 23:04:09 +00:00
|
|
|
bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c,
|
2022-08-25 04:21:19 +00:00
|
|
|
struct backend_shadow_context *sctx) {
|
2019-09-19 17:48:12 +00:00
|
|
|
assert(!w->shadow_image);
|
|
|
|
assert(w->shadow);
|
2022-08-25 16:59:19 +00:00
|
|
|
if ((w->corner_radius == 0 && w->bounding_shaped == false) ||
|
|
|
|
b->ops->shadow_from_mask == NULL) {
|
|
|
|
w->shadow_image = b->ops->render_shadow(b, w->widthb, w->heightb, sctx, c);
|
|
|
|
} else {
|
2023-06-29 22:28:45 +00:00
|
|
|
if (!w->mask_image) {
|
2023-08-10 22:09:56 +00:00
|
|
|
// It's possible we already allocated a mask because of background
|
|
|
|
// blur
|
2023-06-29 22:28:45 +00:00
|
|
|
win_bind_mask(b, w);
|
|
|
|
}
|
2022-08-25 16:59:19 +00:00
|
|
|
w->shadow_image = b->ops->shadow_from_mask(b, w->mask_image, sctx, c);
|
|
|
|
}
|
2019-09-19 17:48:12 +00:00
|
|
|
if (!w->shadow_image) {
|
2022-08-25 16:59:19 +00:00
|
|
|
log_error("Failed to bind shadow image, shadow will be disabled "
|
|
|
|
"for "
|
2020-02-07 21:35:34 +00:00
|
|
|
"%#010x (%s)",
|
|
|
|
w->base.id, w->name);
|
|
|
|
w->shadow = false;
|
2019-09-19 17:48:12 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_debug("New shadow for %#010x (%s)", w->base.id, w->name);
|
|
|
|
return true;
|
2019-03-16 20:14:54 +00:00
|
|
|
}
|
|
|
|
|
2019-09-19 17:48:12 +00:00
|
|
|
void win_release_images(struct backend_base *backend, struct managed_win *w) {
|
2022-08-25 16:59:19 +00:00
|
|
|
// We don't want to decide what we should do if the image we want to
|
|
|
|
// release is stale (do we clear the stale flags or not?) But if we are
|
|
|
|
// not releasing any images anyway, we don't care about the stale flags.
|
2019-09-19 17:48:12 +00:00
|
|
|
|
2020-04-06 19:52:32 +00:00
|
|
|
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
|
|
|
|
assert(!win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE));
|
2019-09-19 17:48:12 +00:00
|
|
|
win_release_pixmap(backend, w);
|
|
|
|
}
|
|
|
|
|
2024-02-10 16:48:37 +00:00
|
|
|
win_release_shadow(backend, w);
|
2022-08-24 06:35:49 +00:00
|
|
|
win_release_mask(backend, w);
|
2019-09-19 17:48:12 +00:00
|
|
|
}
|
2019-03-16 20:14:54 +00:00
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
/// Returns true if the `prop` property is stale, as well as clears the stale
|
|
|
|
/// flag.
|
2020-10-22 14:48:11 +00:00
|
|
|
static bool win_fetch_and_unset_property_stale(struct managed_win *w, xcb_atom_t prop);
|
2022-08-25 16:59:19 +00:00
|
|
|
/// Returns true if any of the properties are stale, as well as clear all the
|
|
|
|
/// stale flags.
|
2020-10-23 00:51:28 +00:00
|
|
|
static void win_clear_all_properties_stale(struct managed_win *w);
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2024-02-19 17:45:34 +00:00
|
|
|
// TODO(yshui) make WIN_FLAGS_FACTOR_CHANGED more fine-grained, or find a better
|
|
|
|
// alternative
|
|
|
|
// way to do all this.
|
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
/// Fetch new window properties from the X server, and run appropriate updates.
|
|
|
|
/// Might set WIN_FLAGS_FACTOR_CHANGED
|
2020-10-23 00:27:55 +00:00
|
|
|
static void win_update_properties(session_t *ps, struct managed_win *w) {
|
2024-03-22 02:41:48 +00:00
|
|
|
// we cannot receive property change when window has been destroyed
|
|
|
|
assert(w->state != WSTATE_DESTROYED);
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
auto c = session_get_x_connection(ps);
|
|
|
|
auto options = session_get_options(ps);
|
|
|
|
auto atoms = session_get_atoms(ps);
|
|
|
|
if (win_fetch_and_unset_property_stale(w, atoms->a_NET_WM_WINDOW_TYPE)) {
|
|
|
|
if (win_update_wintype(c, atoms, w)) {
|
2024-02-19 17:34:07 +00:00
|
|
|
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
|
|
|
|
}
|
2019-09-19 17:48:12 +00:00
|
|
|
}
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (win_fetch_and_unset_property_stale(w, atoms->a_NET_WM_WINDOW_OPACITY)) {
|
|
|
|
win_update_opacity_prop(c, atoms, w, options->detect_client_opacity);
|
2020-10-23 00:27:55 +00:00
|
|
|
win_update_opacity_target(ps, w);
|
2020-10-22 14:48:11 +00:00
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (win_fetch_and_unset_property_stale(w, atoms->a_NET_FRAME_EXTENTS)) {
|
|
|
|
win_update_frame_extents(c, atoms, w, w->client_win, options->frame_opacity);
|
2020-10-23 00:27:55 +00:00
|
|
|
add_damage_from_win(ps, w);
|
|
|
|
}
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (win_fetch_and_unset_property_stale(w, atoms->aWM_NAME) ||
|
|
|
|
win_fetch_and_unset_property_stale(w, atoms->a_NET_WM_NAME)) {
|
|
|
|
if (win_update_name(c, atoms, w) == 1) {
|
2020-10-23 00:27:55 +00:00
|
|
|
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
|
2020-10-22 14:48:11 +00:00
|
|
|
}
|
2020-10-23 00:27:55 +00:00
|
|
|
}
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (win_fetch_and_unset_property_stale(w, atoms->aWM_CLASS)) {
|
|
|
|
if (win_update_class(c, atoms, w)) {
|
2020-10-23 00:27:55 +00:00
|
|
|
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
|
2020-10-22 14:48:11 +00:00
|
|
|
}
|
2020-10-23 00:27:55 +00:00
|
|
|
}
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (win_fetch_and_unset_property_stale(w, atoms->aWM_WINDOW_ROLE)) {
|
|
|
|
if (win_update_role(c, atoms, w) == 1) {
|
2020-10-23 00:27:55 +00:00
|
|
|
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
|
2020-10-22 14:48:11 +00:00
|
|
|
}
|
2020-10-23 00:27:55 +00:00
|
|
|
}
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (win_fetch_and_unset_property_stale(w, atoms->a_COMPTON_SHADOW)) {
|
|
|
|
if (win_update_prop_shadow(c, atoms, w)) {
|
2024-02-19 17:45:34 +00:00
|
|
|
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
|
|
|
|
}
|
2020-10-23 00:27:55 +00:00
|
|
|
}
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (win_fetch_and_unset_property_stale(w, atoms->a_NET_WM_STATE)) {
|
|
|
|
if (win_update_prop_fullscreen(c, atoms, w)) {
|
2024-02-14 18:38:20 +00:00
|
|
|
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (win_fetch_and_unset_property_stale(w, atoms->aWM_CLIENT_LEADER) ||
|
|
|
|
win_fetch_and_unset_property_stale(w, atoms->aWM_TRANSIENT_FOR)) {
|
|
|
|
auto new_leader = win_get_leader_property(c, atoms, w->client_win,
|
|
|
|
options->detect_transient,
|
|
|
|
options->detect_client_leader);
|
2024-02-19 18:11:42 +00:00
|
|
|
if (w->leader != new_leader) {
|
|
|
|
win_set_leader(ps, w, new_leader);
|
|
|
|
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
|
|
|
|
}
|
2020-10-23 00:27:55 +00:00
|
|
|
}
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2020-10-23 00:51:28 +00:00
|
|
|
win_clear_all_properties_stale(w);
|
2020-10-23 00:27:55 +00:00
|
|
|
}
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2020-10-23 03:11:52 +00:00
|
|
|
/// Handle non-image flags. This phase might set IMAGES_STALE flags
|
2020-10-23 00:27:55 +00:00
|
|
|
void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
2024-03-22 02:41:48 +00:00
|
|
|
log_trace("Processing flags for window %#010x (%s), was rendered: %d, flags: "
|
2024-02-13 23:10:16 +00:00
|
|
|
"%#" PRIx64,
|
2024-03-22 02:41:48 +00:00
|
|
|
w->base.id, w->name, w->to_paint, w->flags);
|
2020-12-03 23:53:10 +00:00
|
|
|
|
2020-10-23 00:27:55 +00:00
|
|
|
if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
|
|
|
|
map_win_start(ps, w);
|
|
|
|
win_clear_flags(w, WIN_FLAGS_MAPPED);
|
|
|
|
}
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2024-03-22 02:41:48 +00:00
|
|
|
if (w->state != WSTATE_MAPPED) {
|
2024-03-19 18:01:17 +00:00
|
|
|
// Window is not mapped, so we ignore all its changes until it's mapped
|
|
|
|
// again.
|
2020-11-27 05:34:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// Check client first, because later property updates need accurate client
|
|
|
|
// window information
|
2024-03-24 02:02:14 +00:00
|
|
|
auto c = session_get_x_connection(ps);
|
|
|
|
auto options = session_get_options(ps);
|
|
|
|
auto atoms = session_get_atoms(ps);
|
2020-10-23 00:27:55 +00:00
|
|
|
if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) {
|
2024-03-24 02:02:14 +00:00
|
|
|
auto client_win = win_get_client_window(c, atoms, w);
|
2024-02-19 22:56:07 +00:00
|
|
|
if (w->client_win && w->client_win != client_win) {
|
|
|
|
win_unmark_client(ps, w);
|
|
|
|
}
|
|
|
|
win_mark_client(ps, w, client_win);
|
2020-10-23 00:27:55 +00:00
|
|
|
win_clear_flags(w, WIN_FLAGS_CLIENT_STALE);
|
|
|
|
}
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2020-10-23 03:11:52 +00:00
|
|
|
bool damaged = false;
|
2020-12-03 23:53:10 +00:00
|
|
|
if (win_check_flags_any(w, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE)) {
|
2024-03-22 02:41:48 +00:00
|
|
|
// For damage calculation purposes, we don't care if the window
|
|
|
|
// is mapped in X server, we only care if we rendered it last
|
|
|
|
// frame.
|
|
|
|
//
|
|
|
|
// We do not process window flags for unmapped windows even when
|
|
|
|
// it was rendered, so an window fading out won't move even if the
|
|
|
|
// underlying unmapped window is moved. When the window is
|
|
|
|
// mapped again when it's still fading out, it should have the
|
|
|
|
// same effect as a mapped window being moved, meaning we have
|
|
|
|
// to add both the previous and the new window extents to
|
|
|
|
// damage.
|
|
|
|
//
|
|
|
|
// All that is basically me saying what really matters is if the
|
|
|
|
// window was rendered last frame, not if it's mapped in X server.
|
|
|
|
if (w->to_paint) {
|
2022-08-25 16:59:19 +00:00
|
|
|
// Mark the old extents of this window as damaged. The new
|
|
|
|
// extents will be marked damaged below, after the window
|
|
|
|
// extents are updated.
|
2020-12-03 23:53:10 +00:00
|
|
|
add_damage_from_win(ps, w);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update window geometry
|
|
|
|
w->g = w->pending_g;
|
2020-12-05 11:08:49 +00:00
|
|
|
|
2024-02-14 18:54:04 +00:00
|
|
|
// Whether a window is fullscreen changes based on its geometry
|
|
|
|
win_update_is_fullscreen(ps, w);
|
|
|
|
|
2020-12-05 22:59:47 +00:00
|
|
|
if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) {
|
2024-03-24 02:02:14 +00:00
|
|
|
win_on_win_size_change(w, options->shadow_offset_x,
|
|
|
|
options->shadow_offset_y,
|
|
|
|
options->shadow_radius);
|
|
|
|
win_update_bounding_shape(c, w, session_has_shape_extension(ps),
|
|
|
|
options->detect_rounded_corners);
|
2020-12-05 22:59:47 +00:00
|
|
|
damaged = true;
|
|
|
|
win_clear_flags(w, WIN_FLAGS_SIZE_STALE);
|
2024-02-19 23:09:25 +00:00
|
|
|
|
|
|
|
// Window shape/size changed, invalidate the images we built
|
|
|
|
// log_trace("free out dated pict");
|
|
|
|
win_set_flags(w, WIN_FLAGS_PIXMAP_STALE | WIN_FLAGS_FACTOR_CHANGED);
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
auto backend_data = session_get_backend_data(ps);
|
|
|
|
win_release_mask(backend_data, w);
|
|
|
|
win_release_shadow(backend_data, w);
|
|
|
|
session_mark_updates_pending(ps);
|
2024-02-19 23:09:25 +00:00
|
|
|
free_paint(ps, &w->paint);
|
|
|
|
free_paint(ps, &w->shadow_paint);
|
2020-12-05 22:59:47 +00:00
|
|
|
}
|
2020-12-03 23:53:10 +00:00
|
|
|
|
2020-12-05 22:59:47 +00:00
|
|
|
if (win_check_flags_all(w, WIN_FLAGS_POSITION_STALE)) {
|
|
|
|
damaged = true;
|
|
|
|
win_clear_flags(w, WIN_FLAGS_POSITION_STALE);
|
|
|
|
}
|
2020-10-22 14:48:11 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
win_update_monitor(session_get_monitors(ps), w);
|
2020-10-23 03:11:52 +00:00
|
|
|
}
|
|
|
|
|
2020-10-23 00:27:55 +00:00
|
|
|
if (win_check_flags_all(w, WIN_FLAGS_PROPERTY_STALE)) {
|
|
|
|
win_update_properties(ps, w);
|
|
|
|
win_clear_flags(w, WIN_FLAGS_PROPERTY_STALE);
|
|
|
|
}
|
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// Factor change flags could be set by previous stages, so must be handled
|
|
|
|
// last
|
2020-10-23 00:27:55 +00:00
|
|
|
if (win_check_flags_all(w, WIN_FLAGS_FACTOR_CHANGED)) {
|
|
|
|
win_on_factor_change(ps, w);
|
|
|
|
win_clear_flags(w, WIN_FLAGS_FACTOR_CHANGED);
|
2020-10-22 14:48:11 +00:00
|
|
|
}
|
2020-10-23 03:11:52 +00:00
|
|
|
|
|
|
|
// Add damage, has to be done last so the window has the latest geometry
|
|
|
|
// information.
|
|
|
|
if (damaged) {
|
|
|
|
add_damage_from_win(ps, w);
|
|
|
|
}
|
2020-08-27 16:44:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void win_process_image_flags(session_t *ps, struct managed_win *w) {
|
2024-03-22 02:41:48 +00:00
|
|
|
// Assert that the MAPPED flag is already handled.
|
2020-08-27 16:44:58 +00:00
|
|
|
assert(!win_check_flags_all(w, WIN_FLAGS_MAPPED));
|
2019-09-19 17:48:12 +00:00
|
|
|
|
2024-03-22 02:41:48 +00:00
|
|
|
if (w->state != WSTATE_MAPPED) {
|
2020-11-27 05:34:54 +00:00
|
|
|
// Flags of invisible windows are processed when they are mapped
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-09-19 17:48:12 +00:00
|
|
|
// Not a loop
|
2024-03-24 02:02:14 +00:00
|
|
|
auto backend_data = session_get_backend_data(ps);
|
2024-02-10 16:48:37 +00:00
|
|
|
while (win_check_flags_any(w, WIN_FLAGS_PIXMAP_STALE) &&
|
2020-04-06 19:52:32 +00:00
|
|
|
!win_check_flags_all(w, WIN_FLAGS_IMAGE_ERROR)) {
|
2019-09-19 15:59:36 +00:00
|
|
|
// Image needs to be updated, update it.
|
2024-03-24 02:02:14 +00:00
|
|
|
if (!backend_data) {
|
2019-09-23 18:44:35 +00:00
|
|
|
// We are using legacy backend, nothing to do here.
|
2019-09-19 17:48:12 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-04-06 19:52:32 +00:00
|
|
|
if (win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE)) {
|
2022-08-25 16:59:19 +00:00
|
|
|
// Check to make sure the window is still mapped,
|
|
|
|
// otherwise we won't be able to rebind pixmap after
|
|
|
|
// releasing it, yet we might still need the pixmap for
|
|
|
|
// rendering.
|
2020-04-06 19:52:32 +00:00
|
|
|
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
|
2019-09-28 23:09:40 +00:00
|
|
|
// Must release images first, otherwise breaks
|
|
|
|
// NVIDIA driver
|
2024-03-24 02:02:14 +00:00
|
|
|
win_release_pixmap(backend_data, w);
|
2019-09-19 17:48:12 +00:00
|
|
|
}
|
2024-03-24 02:02:14 +00:00
|
|
|
win_bind_pixmap(backend_data, w);
|
2019-09-19 17:48:12 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 18:44:35 +00:00
|
|
|
// break here, loop always run only once
|
|
|
|
break;
|
2019-09-19 15:59:36 +00:00
|
|
|
}
|
2019-09-23 18:44:35 +00:00
|
|
|
|
|
|
|
// Clear stale image flags
|
2024-02-10 16:48:37 +00:00
|
|
|
if (win_check_flags_any(w, WIN_FLAGS_PIXMAP_STALE)) {
|
|
|
|
win_clear_flags(w, WIN_FLAGS_PIXMAP_STALE);
|
2020-04-10 00:44:33 +00:00
|
|
|
}
|
2019-09-19 15:59:36 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 18:17:26 +00:00
|
|
|
/**
|
|
|
|
* Check if a window has rounded corners.
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 03:56:00 +00:00
|
|
|
* XXX This is really dumb
|
2018-09-06 18:17:26 +00:00
|
|
|
*/
|
2019-04-17 22:14:45 +00:00
|
|
|
static bool attr_pure win_has_rounded_corners(const struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
if (!w->bounding_shaped) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Quit if border_size() returns XCB_NONE
|
|
|
|
if (!pixman_region32_not_empty((region_t *)&w->bounding_shape)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine the minimum width/height of a rectangle that could mark
|
|
|
|
// a window as having rounded corners
|
2019-03-30 09:07:21 +00:00
|
|
|
auto minwidth =
|
|
|
|
(uint16_t)max2(w->widthb * (1 - ROUNDED_PERCENT), w->widthb - ROUNDED_PIXELS);
|
|
|
|
auto minheight =
|
|
|
|
(uint16_t)max2(w->heightb * (1 - ROUNDED_PERCENT), w->heightb - ROUNDED_PIXELS);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Get the rectangles in the bounding region
|
|
|
|
int nrects = 0;
|
|
|
|
const rect_t *rects =
|
|
|
|
pixman_region32_rectangles((region_t *)&w->bounding_shape, &nrects);
|
|
|
|
|
|
|
|
// Look for a rectangle large enough for this window be considered
|
|
|
|
// having rounded corners
|
|
|
|
for (int i = 0; i < nrects; ++i) {
|
|
|
|
if (rects[i].x2 - rects[i].x1 >= minwidth &&
|
|
|
|
rects[i].y2 - rects[i].y1 >= minheight) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2024-02-19 17:17:35 +00:00
|
|
|
int win_update_name(struct x_connection *c, struct atom *atoms, struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
char **strlst = NULL;
|
|
|
|
int nstr = 0;
|
|
|
|
|
2020-10-22 00:39:51 +00:00
|
|
|
if (!w->client_win) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return 0;
|
2020-10-22 00:39:51 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2024-02-19 17:17:35 +00:00
|
|
|
if (!(wid_get_text_prop(c, atoms, w->client_win, atoms->a_NET_WM_NAME, &strlst, &nstr))) {
|
2022-08-25 16:59:19 +00:00
|
|
|
log_debug("(%#010x): _NET_WM_NAME unset, falling back to "
|
|
|
|
"WM_NAME.",
|
2019-03-10 12:34:37 +00:00
|
|
|
w->client_win);
|
|
|
|
|
2024-02-19 17:17:35 +00:00
|
|
|
if (!wid_get_text_prop(c, atoms, w->client_win, atoms->aWM_NAME, &strlst,
|
|
|
|
&nstr)) {
|
2020-10-23 14:14:17 +00:00
|
|
|
log_debug("Unsetting window name for %#010x", w->client_win);
|
|
|
|
free(w->name);
|
|
|
|
w->name = NULL;
|
2019-03-10 12:34:37 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
if (!w->name || strcmp(w->name, strlst[0]) != 0) {
|
|
|
|
ret = 1;
|
|
|
|
free(w->name);
|
|
|
|
w->name = strdup(strlst[0]);
|
|
|
|
}
|
|
|
|
|
2020-10-22 00:39:51 +00:00
|
|
|
free(strlst);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2020-10-23 14:14:17 +00:00
|
|
|
log_debug("(%#010x): client = %#010x, name = \"%s\", "
|
2019-03-10 12:34:37 +00:00
|
|
|
"ret = %d",
|
2019-04-17 22:14:45 +00:00
|
|
|
w->base.id, w->client_win, w->name, ret);
|
2019-03-10 12:34:37 +00:00
|
|
|
return ret;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2024-02-20 00:34:26 +00:00
|
|
|
int win_update_role(struct x_connection *c, struct atom *atoms, struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
char **strlst = NULL;
|
|
|
|
int nstr = 0;
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2024-02-19 17:17:35 +00:00
|
|
|
if (!wid_get_text_prop(c, atoms, w->client_win, atoms->aWM_WINDOW_ROLE, &strlst, &nstr)) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return -1;
|
2020-10-22 00:39:51 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
int ret = 0;
|
|
|
|
if (!w->role || strcmp(w->role, strlst[0]) != 0) {
|
|
|
|
ret = 1;
|
|
|
|
free(w->role);
|
|
|
|
w->role = strdup(strlst[0]);
|
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2020-10-22 00:39:51 +00:00
|
|
|
free(strlst);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
log_trace("(%#010x): client = %#010x, role = \"%s\", "
|
|
|
|
"ret = %d",
|
2019-04-17 22:14:45 +00:00
|
|
|
w->base.id, w->client_win, w->role, ret);
|
2019-03-10 12:34:37 +00:00
|
|
|
return ret;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a window is bounding-shaped.
|
|
|
|
*/
|
2024-02-19 23:01:06 +00:00
|
|
|
static inline bool win_bounding_shaped(struct x_connection *c, xcb_window_t wid) {
|
|
|
|
xcb_shape_query_extents_reply_t *reply;
|
|
|
|
bool bounding_shaped;
|
|
|
|
reply = xcb_shape_query_extents_reply(c->c, xcb_shape_query_extents(c->c, wid), NULL);
|
|
|
|
bounding_shaped = reply && reply->bounding_shaped;
|
|
|
|
free(reply);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2024-02-19 23:01:06 +00:00
|
|
|
return bounding_shaped;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2024-02-19 17:34:07 +00:00
|
|
|
static wintype_t
|
|
|
|
wid_get_prop_wintype(struct x_connection *c, struct atom *atoms, xcb_window_t wid) {
|
2019-05-05 23:34:08 +00:00
|
|
|
winprop_t prop =
|
2024-02-19 17:34:07 +00:00
|
|
|
x_get_prop(c, wid, atoms->a_NET_WM_WINDOW_TYPE, 32L, XCB_ATOM_ATOM, 32);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
for (unsigned i = 0; i < prop.nitems; ++i) {
|
|
|
|
for (wintype_t j = 1; j < NUM_WINTYPES; ++j) {
|
2024-02-20 00:41:55 +00:00
|
|
|
if (get_atom_with_nul(atoms, WINTYPES[j].atom, c->c) ==
|
2024-02-19 17:34:07 +00:00
|
|
|
(xcb_atom_t)prop.p32[i]) {
|
2019-03-10 12:34:37 +00:00
|
|
|
free_winprop(&prop);
|
|
|
|
return j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
free_winprop(&prop);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
return WINTYPE_UNKNOWN;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2024-02-19 17:17:35 +00:00
|
|
|
static bool wid_get_opacity_prop(struct x_connection *c, struct atom *atoms,
|
|
|
|
xcb_window_t wid, opacity_t def, opacity_t *out) {
|
2019-03-10 12:34:37 +00:00
|
|
|
bool ret = false;
|
|
|
|
*out = def;
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2024-02-19 17:17:35 +00:00
|
|
|
winprop_t prop =
|
|
|
|
x_get_prop(c, wid, atoms->a_NET_WM_WINDOW_OPACITY, 1L, XCB_ATOM_CARDINAL, 32);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
if (prop.nitems) {
|
|
|
|
*out = *prop.c32;
|
|
|
|
ret = true;
|
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
free_winprop(&prop);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
return ret;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2018-09-29 18:07:39 +00:00
|
|
|
// XXX should distinguish between frame has alpha and window body has alpha
|
2019-04-17 22:14:45 +00:00
|
|
|
bool win_has_alpha(const struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return w->pictfmt && w->pictfmt->type == XCB_RENDER_PICT_TYPE_DIRECT &&
|
|
|
|
w->pictfmt->direct.alpha_mask;
|
2018-09-29 18:07:39 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-07-13 11:29:39 +00:00
|
|
|
bool win_client_has_alpha(const struct managed_win *w) {
|
|
|
|
return w->client_pictfmt && w->client_pictfmt->type == XCB_RENDER_PICT_TYPE_DIRECT &&
|
|
|
|
w->client_pictfmt->direct.alpha_mask;
|
|
|
|
}
|
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
winmode_t win_calc_mode(const struct managed_win *w) {
|
2024-03-19 16:44:02 +00:00
|
|
|
if (animatable_get(&w->opacity) < 1.0) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return WMODE_TRANS;
|
|
|
|
}
|
2019-07-13 11:29:39 +00:00
|
|
|
|
|
|
|
if (win_has_alpha(w)) {
|
2019-10-24 18:30:11 +00:00
|
|
|
if (w->client_win == XCB_NONE) {
|
2022-08-25 16:59:19 +00:00
|
|
|
// This is a window not managed by the WM, and it has
|
|
|
|
// alpha, so it's transparent. No need to check WM frame.
|
2019-10-24 18:30:11 +00:00
|
|
|
return WMODE_TRANS;
|
|
|
|
}
|
2019-07-13 11:29:39 +00:00
|
|
|
// The WM window has alpha
|
|
|
|
if (win_client_has_alpha(w)) {
|
|
|
|
// The client window also has alpha, the entire window is
|
|
|
|
// transparent
|
|
|
|
return WMODE_TRANS;
|
|
|
|
}
|
|
|
|
if (win_has_frame(w)) {
|
2022-08-25 16:59:19 +00:00
|
|
|
// The client window doesn't have alpha, but we have a WM
|
|
|
|
// frame window, which has alpha.
|
2019-07-13 11:29:39 +00:00
|
|
|
return WMODE_FRAME_TRANS;
|
|
|
|
}
|
2022-08-25 16:59:19 +00:00
|
|
|
// Although the WM window has alpha, the frame window has 0 size,
|
|
|
|
// so consider the window solid
|
2019-07-13 11:29:39 +00:00
|
|
|
}
|
|
|
|
|
2020-03-11 21:00:47 +00:00
|
|
|
if (w->frame_opacity != 1.0 && win_has_frame(w)) {
|
|
|
|
return WMODE_FRAME_TRANS;
|
|
|
|
}
|
|
|
|
|
2019-07-26 22:40:30 +00:00
|
|
|
// log_trace("Window %#010x(%s) is solid", w->client_win, w->name);
|
2019-03-10 12:34:37 +00:00
|
|
|
return WMODE_SOLID;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-02-09 15:50:40 +00:00
|
|
|
* Calculate and return the opacity target of a window.
|
2018-09-06 18:17:26 +00:00
|
|
|
*
|
2019-07-26 22:40:30 +00:00
|
|
|
* The priority of opacity settings are:
|
2018-09-06 18:17:26 +00:00
|
|
|
*
|
2022-08-25 16:59:19 +00:00
|
|
|
* inactive_opacity_override (if set, and unfocused) > _NET_WM_WINDOW_OPACITY (if
|
|
|
|
* set) > opacity-rules (if matched) > window type default opacity >
|
|
|
|
* active/inactive opacity
|
2018-09-06 18:17:26 +00:00
|
|
|
*
|
2019-07-26 22:40:30 +00:00
|
|
|
* @param ps current session
|
|
|
|
* @param w struct _win object representing the window
|
2019-02-09 15:50:40 +00:00
|
|
|
*
|
|
|
|
* @return target opacity
|
2018-09-06 18:17:26 +00:00
|
|
|
*/
|
2024-03-19 16:44:02 +00:00
|
|
|
static double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
double opacity = 1;
|
|
|
|
|
2024-03-22 02:41:48 +00:00
|
|
|
if (w->state == WSTATE_UNMAPPED || w->state == WSTATE_DESTROYED) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// be consistent
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// Try obeying opacity property and window type opacity firstly
|
2024-03-24 02:02:14 +00:00
|
|
|
auto options = session_get_options(ps);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (w->has_opacity_prop) {
|
|
|
|
opacity = ((double)w->opacity_prop) / OPAQUE;
|
2019-07-26 21:28:53 +00:00
|
|
|
} else if (w->opacity_is_set) {
|
|
|
|
opacity = w->opacity_set;
|
2024-03-24 02:02:14 +00:00
|
|
|
} else if (!safe_isnan(options->wintype_option[w->window_type].opacity)) {
|
|
|
|
opacity = options->wintype_option[w->window_type].opacity;
|
2019-03-10 12:34:37 +00:00
|
|
|
} else {
|
2022-08-25 16:59:19 +00:00
|
|
|
// Respect active_opacity only when the window is physically
|
|
|
|
// focused
|
2024-02-15 00:07:31 +00:00
|
|
|
if (win_is_focused_raw(w)) {
|
2024-03-24 02:02:14 +00:00
|
|
|
opacity = options->active_opacity;
|
2023-06-29 04:39:36 +00:00
|
|
|
} else if (!w->focused) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// Respect inactive_opacity in some cases
|
2024-03-24 02:02:14 +00:00
|
|
|
opacity = options->inactive_opacity;
|
2023-06-29 04:39:36 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// respect inactive override
|
2024-03-24 02:02:14 +00:00
|
|
|
if (options->inactive_opacity_override && !w->focused) {
|
|
|
|
opacity = options->inactive_opacity;
|
2019-07-26 22:40:30 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
return opacity;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2024-03-22 02:41:48 +00:00
|
|
|
/// Finish the unmapping of a window (e.g. after fading has finished).
|
|
|
|
/// Doesn't free `w`
|
|
|
|
static void unmap_win_finish(session_t *ps, struct managed_win *w) {
|
|
|
|
w->reg_ignore_valid = false;
|
|
|
|
w->state = WSTATE_UNMAPPED;
|
|
|
|
|
|
|
|
// We are in unmap_win, this window definitely was viewable
|
2024-03-24 02:02:14 +00:00
|
|
|
auto backend_data = session_get_backend_data(ps);
|
|
|
|
if (backend_data) {
|
2024-03-22 02:41:48 +00:00
|
|
|
// Only the pixmap needs to be freed and reacquired when mapping.
|
|
|
|
// Shadow image can be preserved.
|
|
|
|
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
|
2024-03-24 02:02:14 +00:00
|
|
|
win_release_pixmap(backend_data, w);
|
2024-03-22 02:41:48 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
assert(!w->win_image);
|
|
|
|
assert(!w->shadow_image);
|
|
|
|
}
|
|
|
|
|
|
|
|
free_paint(ps, &w->paint);
|
|
|
|
free_paint(ps, &w->shadow_paint);
|
|
|
|
|
|
|
|
// Try again at binding images when the window is mapped next time
|
|
|
|
win_clear_flags(w, WIN_FLAGS_IMAGE_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct window_transition_data {
|
|
|
|
struct managed_win *w;
|
|
|
|
session_t *ps;
|
|
|
|
// TODO(yshui) switch to only pass backend_data after the legacy backend removal
|
|
|
|
// struct backend_base *backend_data;
|
|
|
|
uint64_t refcount;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void win_transition_callback(enum transition_event event attr_unused, void *data_) {
|
|
|
|
auto data = (struct window_transition_data *)data_;
|
|
|
|
auto w = data->w;
|
2024-03-21 18:01:02 +00:00
|
|
|
w->number_of_animations--;
|
2024-03-22 02:41:48 +00:00
|
|
|
if (w->number_of_animations == 0) {
|
|
|
|
if (w->state == WSTATE_DESTROYED || w->state == WSTATE_UNMAPPED) {
|
|
|
|
if (animatable_get(&w->opacity) != 0) {
|
|
|
|
log_warn("Window %#010x (%s) has finished fading out but "
|
|
|
|
"its opacity is not 0",
|
|
|
|
w->base.id, w->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (w->state == WSTATE_UNMAPPED) {
|
|
|
|
unmap_win_finish(data->ps, data->w);
|
|
|
|
}
|
|
|
|
// Destroyed windows are freed in paint_preprocess, this makes managing
|
|
|
|
// the lifetime of windows easier.
|
|
|
|
w->in_openclose = false;
|
|
|
|
}
|
|
|
|
data->refcount--;
|
|
|
|
if (data->refcount == 0) {
|
|
|
|
free(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if a window should fade on opacity change.
|
|
|
|
*/
|
|
|
|
bool win_should_fade(session_t *ps, const struct managed_win *w) {
|
|
|
|
// To prevent it from being overwritten by last-paint value if the window
|
|
|
|
// is
|
|
|
|
if (w->fade_force != UNSET) {
|
|
|
|
return w->fade_force;
|
|
|
|
}
|
2024-03-24 02:02:14 +00:00
|
|
|
|
|
|
|
auto options = session_get_options(ps);
|
|
|
|
if (options->no_fading_openclose && w->in_openclose) {
|
2024-03-22 02:41:48 +00:00
|
|
|
return false;
|
|
|
|
}
|
2024-03-24 02:02:14 +00:00
|
|
|
if (options->no_fading_destroyed_argb && w->state == WSTATE_DESTROYED &&
|
2024-03-22 02:41:48 +00:00
|
|
|
win_has_alpha(w) && w->client_win && w->client_win != w->base.id) {
|
|
|
|
// deprecated
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (w->fade_excluded) {
|
|
|
|
return false;
|
|
|
|
}
|
2024-03-24 02:02:14 +00:00
|
|
|
return options->wintype_option[w->window_type].fade;
|
2024-03-21 18:01:02 +00:00
|
|
|
}
|
|
|
|
|
2024-03-19 16:44:02 +00:00
|
|
|
/// Call `animatable_set_target` on the opacity of a window, with appropriate
|
|
|
|
/// target opacity and duration.
|
2024-03-22 02:41:48 +00:00
|
|
|
static inline void
|
|
|
|
win_start_fade(session_t *ps, struct managed_win *w, double target_blur_opacity) {
|
2024-03-19 16:44:02 +00:00
|
|
|
double current_opacity = animatable_get(&w->opacity),
|
|
|
|
target_opacity = win_calc_opacity_target(ps, w);
|
2024-03-24 02:02:14 +00:00
|
|
|
auto options = session_get_options(ps);
|
|
|
|
double step_size = target_opacity > current_opacity ? options->fade_in_step
|
|
|
|
: options->fade_out_step;
|
2024-03-19 16:44:02 +00:00
|
|
|
unsigned int duration =
|
|
|
|
(unsigned int)(fabs(target_opacity - current_opacity) / step_size);
|
2024-03-22 02:41:48 +00:00
|
|
|
if (!win_should_fade(ps, w)) {
|
|
|
|
duration = 0;
|
|
|
|
}
|
|
|
|
auto data = ccalloc(1, struct window_transition_data);
|
|
|
|
data->ps = ps;
|
|
|
|
data->w = w;
|
|
|
|
data->refcount = 1;
|
|
|
|
|
|
|
|
animatable_cancel(&w->blur_opacity); // Cancel any ongoing blur animation
|
|
|
|
// so we can check its current value
|
|
|
|
|
|
|
|
// We want to set the correct `number_of_animations` before calling
|
|
|
|
// `animatable_set_target`, because it might trigger our callback which will
|
|
|
|
// decrement `number_of_animations`
|
2024-03-21 18:01:02 +00:00
|
|
|
w->number_of_animations++;
|
2024-03-22 02:41:48 +00:00
|
|
|
if (target_blur_opacity != w->blur_opacity.start) {
|
|
|
|
w->number_of_animations++;
|
|
|
|
data->refcount++;
|
|
|
|
}
|
|
|
|
|
2024-03-21 18:00:16 +00:00
|
|
|
animatable_set_target(&w->opacity, target_opacity, duration,
|
2024-03-22 02:41:48 +00:00
|
|
|
win_transition_callback, data);
|
|
|
|
if (target_blur_opacity != w->blur_opacity.start) {
|
|
|
|
animatable_set_target(&w->blur_opacity, target_blur_opacity, duration,
|
|
|
|
win_transition_callback, data);
|
|
|
|
}
|
2024-03-19 16:44:02 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 18:17:26 +00:00
|
|
|
/**
|
|
|
|
* Determine whether a window is to be dimmed.
|
|
|
|
*/
|
2019-04-17 22:14:45 +00:00
|
|
|
bool win_should_dim(session_t *ps, const struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// Make sure we do nothing if the window is unmapped / being destroyed
|
2024-03-24 02:02:14 +00:00
|
|
|
return w->state != WSTATE_UNMAPPED && session_get_options(ps)->inactive_dim > 0 &&
|
|
|
|
!w->focused;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reread _COMPTON_SHADOW property from a window.
|
|
|
|
*
|
|
|
|
* The property must be set on the outermost window, usually the WM frame.
|
|
|
|
*/
|
2024-02-19 17:17:35 +00:00
|
|
|
void win_update_prop_shadow_raw(struct x_connection *c, struct atom *atoms,
|
|
|
|
struct managed_win *w) {
|
|
|
|
winprop_t prop =
|
|
|
|
x_get_prop(c, w->base.id, atoms->a_COMPTON_SHADOW, 1, XCB_ATOM_CARDINAL, 32);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
if (!prop.nitems) {
|
|
|
|
w->prop_shadow = -1;
|
|
|
|
} else {
|
|
|
|
w->prop_shadow = *prop.c32;
|
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
free_winprop(&prop);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2019-08-10 00:44:27 +00:00
|
|
|
static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new) {
|
2019-03-31 21:49:40 +00:00
|
|
|
if (w->shadow == shadow_new) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
2019-03-31 21:49:40 +00:00
|
|
|
}
|
|
|
|
|
2019-04-18 07:51:17 +00:00
|
|
|
log_debug("Updating shadow property of window %#010x (%s) to %d", w->base.id,
|
|
|
|
w->name, shadow_new);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// We don't handle property updates of non-visible windows until they are
|
|
|
|
// mapped.
|
2024-03-22 02:41:48 +00:00
|
|
|
assert(w->state == WSTATE_MAPPED);
|
2019-11-18 22:34:05 +00:00
|
|
|
|
|
|
|
// Keep a copy of window extent before the shadow change. Will be used for
|
|
|
|
// calculation of damaged region
|
2019-03-10 12:34:37 +00:00
|
|
|
region_t extents;
|
|
|
|
pixman_region32_init(&extents);
|
|
|
|
win_extents(w, &extents);
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (session_is_redirected(ps)) {
|
2020-10-23 15:41:20 +00:00
|
|
|
// Add damage for shadow change
|
|
|
|
|
|
|
|
// Window extents need update on shadow state change
|
|
|
|
// Shadow geometry currently doesn't change on shadow state change
|
|
|
|
// calc_shadow_geometry(ps, w);
|
2024-02-10 16:48:37 +00:00
|
|
|
if (shadow_new) {
|
2020-10-23 15:41:20 +00:00
|
|
|
// Mark the new extents as damaged if the shadow is added
|
2024-02-10 16:48:37 +00:00
|
|
|
assert(!w->shadow_image);
|
2020-10-23 15:41:20 +00:00
|
|
|
pixman_region32_clear(&extents);
|
|
|
|
win_extents(w, &extents);
|
|
|
|
add_damage_from_win(ps, w);
|
|
|
|
} else {
|
2022-08-25 16:59:19 +00:00
|
|
|
// Mark the old extents as damaged if the shadow is
|
|
|
|
// removed
|
2020-10-23 15:41:20 +00:00
|
|
|
add_damage(ps, &extents);
|
2024-03-24 02:02:14 +00:00
|
|
|
win_release_shadow(session_get_backend_data(ps), w);
|
2020-10-23 15:41:20 +00:00
|
|
|
}
|
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// Only set pending_updates if we are redirected. Otherwise change
|
|
|
|
// of a shadow won't have influence on whether we should redirect.
|
2024-03-24 02:02:14 +00:00
|
|
|
session_mark_updates_pending(ps);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2019-11-18 22:34:05 +00:00
|
|
|
|
2024-02-10 16:48:37 +00:00
|
|
|
w->shadow = shadow_new;
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
pixman_region32_fini(&extents);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if a window should have shadow, and update things depending
|
|
|
|
* on shadow state.
|
|
|
|
*/
|
2019-08-10 00:44:27 +00:00
|
|
|
static void win_determine_shadow(session_t *ps, struct managed_win *w) {
|
2019-04-17 22:14:45 +00:00
|
|
|
log_debug("Determining shadow of window %#010x (%s)", w->base.id, w->name);
|
2019-03-10 12:34:37 +00:00
|
|
|
bool shadow_new = w->shadow;
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
auto options = session_get_options(ps);
|
|
|
|
auto c2_state = session_get_c2(ps);
|
2019-03-31 21:41:50 +00:00
|
|
|
if (w->shadow_force != UNSET) {
|
2019-03-10 12:34:37 +00:00
|
|
|
shadow_new = w->shadow_force;
|
2019-03-31 21:41:50 +00:00
|
|
|
} else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
|
|
|
|
shadow_new = true;
|
2024-03-24 02:02:14 +00:00
|
|
|
if (!options->wintype_option[w->window_type].shadow) {
|
2019-03-31 21:41:50 +00:00
|
|
|
log_debug("Shadow disabled by wintypes");
|
|
|
|
shadow_new = false;
|
2024-03-24 02:02:14 +00:00
|
|
|
} else if (c2_match(c2_state, w, options->shadow_blacklist, NULL)) {
|
2019-03-31 21:41:50 +00:00
|
|
|
log_debug("Shadow disabled by shadow-exclude");
|
|
|
|
shadow_new = false;
|
2024-03-24 02:02:14 +00:00
|
|
|
} else if (options->shadow_ignore_shaped && w->bounding_shaped &&
|
2019-03-31 21:49:40 +00:00
|
|
|
!w->rounded_corners) {
|
2019-03-31 21:41:50 +00:00
|
|
|
log_debug("Shadow disabled by shadow-ignore-shaped");
|
|
|
|
shadow_new = false;
|
2019-10-22 19:44:59 +00:00
|
|
|
} else if (w->prop_shadow == 0) {
|
2019-03-31 21:41:50 +00:00
|
|
|
log_debug("Shadow disabled by shadow property");
|
|
|
|
shadow_new = false;
|
|
|
|
}
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
win_set_shadow(ps, w, shadow_new);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2019-08-10 00:44:27 +00:00
|
|
|
/**
|
|
|
|
* Reread _COMPTON_SHADOW property from a window and update related
|
|
|
|
* things.
|
|
|
|
*/
|
2024-02-19 17:45:34 +00:00
|
|
|
static bool
|
|
|
|
win_update_prop_shadow(struct x_connection *c, struct atom *atoms, struct managed_win *w) {
|
2021-11-25 08:21:41 +00:00
|
|
|
long long attr_shadow_old = w->prop_shadow;
|
2024-02-19 17:45:34 +00:00
|
|
|
win_update_prop_shadow_raw(c, atoms, w);
|
|
|
|
return w->prop_shadow != attr_shadow_old;
|
2019-08-10 00:44:27 +00:00
|
|
|
}
|
|
|
|
|
2024-02-14 18:38:20 +00:00
|
|
|
/**
|
|
|
|
* Update window EWMH fullscreen state.
|
|
|
|
*/
|
|
|
|
bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms,
|
|
|
|
struct managed_win *w) {
|
|
|
|
auto prop = x_get_prop(c, w->client_win, atoms->a_NET_WM_STATE, 12, XCB_ATOM_ATOM, 0);
|
|
|
|
bool is_fullscreen = false;
|
|
|
|
for (uint32_t i = 0; i < prop.nitems; i++) {
|
|
|
|
if (prop.atom[i] == atoms->a_NET_WM_STATE_FULLSCREEN) {
|
|
|
|
is_fullscreen = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free_winprop(&prop);
|
|
|
|
|
|
|
|
bool changed = w->is_ewmh_fullscreen != is_fullscreen;
|
|
|
|
w->is_ewmh_fullscreen = is_fullscreen;
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2021-07-09 17:17:23 +00:00
|
|
|
static void win_determine_clip_shadow_above(session_t *ps, struct managed_win *w) {
|
2024-03-24 02:02:14 +00:00
|
|
|
auto options = session_get_options(ps);
|
|
|
|
auto c2_state = session_get_c2(ps);
|
|
|
|
bool should_crop = (options->wintype_option[w->window_type].clip_shadow_above ||
|
|
|
|
c2_match(c2_state, w, options->shadow_clip_list, NULL));
|
2021-07-09 17:17:23 +00:00
|
|
|
w->clip_shadow_above = should_crop;
|
|
|
|
}
|
|
|
|
|
2019-08-10 00:44:27 +00:00
|
|
|
static void win_set_invert_color(session_t *ps, struct managed_win *w, bool invert_color_new) {
|
2020-10-23 00:27:55 +00:00
|
|
|
if (w->invert_color == invert_color_new) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
2020-10-23 00:27:55 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
w->invert_color = invert_color_new;
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
add_damage_from_win(ps, w);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2019-08-10 00:44:27 +00:00
|
|
|
/**
|
|
|
|
* Determine if a window should have color inverted.
|
|
|
|
*/
|
|
|
|
static void win_determine_invert_color(session_t *ps, struct managed_win *w) {
|
|
|
|
bool invert_color_new = w->invert_color;
|
2024-03-24 02:02:14 +00:00
|
|
|
auto c2_state = session_get_c2(ps);
|
|
|
|
auto options = session_get_options(ps);
|
2019-08-10 00:44:27 +00:00
|
|
|
|
2020-10-23 00:27:55 +00:00
|
|
|
if (UNSET != w->invert_color_force) {
|
2019-08-10 00:44:27 +00:00
|
|
|
invert_color_new = w->invert_color_force;
|
2020-10-23 00:27:55 +00:00
|
|
|
} else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
|
2024-03-24 02:02:14 +00:00
|
|
|
invert_color_new = c2_match(c2_state, w, options->invert_color_list, NULL);
|
2020-10-23 00:27:55 +00:00
|
|
|
}
|
2019-08-10 00:44:27 +00:00
|
|
|
|
|
|
|
win_set_invert_color(ps, w, invert_color_new);
|
|
|
|
}
|
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
/**
|
|
|
|
* Set w->invert_color_force of a window.
|
|
|
|
*/
|
|
|
|
void win_set_invert_color_force(session_t *ps, struct managed_win *w, switch_t val) {
|
|
|
|
if (val != w->invert_color_force) {
|
|
|
|
w->invert_color_force = val;
|
|
|
|
win_determine_invert_color(ps, w);
|
|
|
|
queue_redraw(ps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set w->fade_force of a window.
|
|
|
|
*
|
|
|
|
* Doesn't affect fading already in progress
|
|
|
|
*/
|
2019-07-25 01:27:02 +00:00
|
|
|
void win_set_fade_force(struct managed_win *w, switch_t val) {
|
2019-04-17 22:14:45 +00:00
|
|
|
w->fade_force = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set w->focused_force of a window.
|
|
|
|
*/
|
|
|
|
void win_set_focused_force(session_t *ps, struct managed_win *w, switch_t val) {
|
|
|
|
if (val != w->focused_force) {
|
|
|
|
w->focused_force = val;
|
2020-03-17 19:50:48 +00:00
|
|
|
win_on_factor_change(ps, w);
|
2019-04-17 22:14:45 +00:00
|
|
|
queue_redraw(ps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set w->shadow_force of a window.
|
|
|
|
*/
|
|
|
|
void win_set_shadow_force(session_t *ps, struct managed_win *w, switch_t val) {
|
|
|
|
if (val != w->shadow_force) {
|
|
|
|
w->shadow_force = val;
|
|
|
|
win_determine_shadow(ps, w);
|
|
|
|
queue_redraw(ps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-26 22:40:30 +00:00
|
|
|
static void
|
|
|
|
win_set_blur_background(session_t *ps, struct managed_win *w, bool blur_background_new) {
|
2023-06-29 04:39:36 +00:00
|
|
|
if (w->blur_background == blur_background_new) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
2023-06-29 04:39:36 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
w->blur_background = blur_background_new;
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// This damage might not be absolutely necessary (e.g. when the window is
|
|
|
|
// opaque), but blur_background changes should be rare, so this should be
|
|
|
|
// fine.
|
2019-07-13 11:29:39 +00:00
|
|
|
add_damage_from_win(ps, w);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2020-10-12 16:34:51 +00:00
|
|
|
static void
|
|
|
|
win_set_fg_shader(session_t *ps, struct managed_win *w, struct shader_info *shader_new) {
|
|
|
|
if (w->fg_shader == shader_new) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->fg_shader = shader_new;
|
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// A different shader might change how the window is drawn, these changes
|
|
|
|
// should be rare however, so this should be fine.
|
2020-10-12 16:34:51 +00:00
|
|
|
add_damage_from_win(ps, w);
|
|
|
|
}
|
|
|
|
|
2018-09-06 18:17:26 +00:00
|
|
|
/**
|
|
|
|
* Determine if a window should have background blurred.
|
|
|
|
*/
|
2019-08-10 00:44:27 +00:00
|
|
|
static void win_determine_blur_background(session_t *ps, struct managed_win *w) {
|
2020-10-01 20:51:15 +00:00
|
|
|
log_debug("Determining blur-background of window %#010x (%s)", w->base.id, w->name);
|
2020-11-27 05:34:54 +00:00
|
|
|
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
2020-11-27 05:34:54 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
auto options = session_get_options(ps);
|
|
|
|
auto c2_state = session_get_c2(ps);
|
|
|
|
bool blur_background_new = options->blur_method != BLUR_METHOD_NONE;
|
2020-10-01 20:51:15 +00:00
|
|
|
if (blur_background_new) {
|
2024-03-24 02:02:14 +00:00
|
|
|
if (!options->wintype_option[w->window_type].blur_background) {
|
2020-10-01 20:51:15 +00:00
|
|
|
log_debug("Blur background disabled by wintypes");
|
|
|
|
blur_background_new = false;
|
2024-03-24 02:02:14 +00:00
|
|
|
} else if (c2_match(c2_state, w, options->blur_background_blacklist, NULL)) {
|
2022-08-25 16:59:19 +00:00
|
|
|
log_debug("Blur background disabled by "
|
|
|
|
"blur-background-exclude");
|
2020-10-01 20:51:15 +00:00
|
|
|
blur_background_new = false;
|
|
|
|
}
|
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
win_set_blur_background(ps, w, blur_background_new);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2020-09-05 14:46:31 +00:00
|
|
|
/**
|
|
|
|
* Determine if a window should have rounded corners.
|
|
|
|
*/
|
|
|
|
static void win_determine_rounded_corners(session_t *ps, struct managed_win *w) {
|
2023-03-29 20:18:41 +00:00
|
|
|
void *radius_override = NULL;
|
2024-03-24 02:02:14 +00:00
|
|
|
auto c2_state = session_get_c2(ps);
|
|
|
|
auto options = session_get_options(ps);
|
|
|
|
if (c2_match(c2_state, w, options->corner_radius_rules, &radius_override)) {
|
2023-03-29 20:18:41 +00:00
|
|
|
log_debug("Matched corner rule! %d", w->corner_radius);
|
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (options->corner_radius == 0 && !radius_override) {
|
2024-01-29 20:16:51 +00:00
|
|
|
w->corner_radius = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-03-29 20:18:41 +00:00
|
|
|
// Don't round full screen windows & excluded windows,
|
|
|
|
// unless we find a corner override in corner_radius_rules
|
2024-02-16 15:19:36 +00:00
|
|
|
if (!radius_override &&
|
|
|
|
((w && w->is_fullscreen) ||
|
2024-03-24 02:02:14 +00:00
|
|
|
c2_match(c2_state, w, options->rounded_corners_blacklist, NULL))) {
|
2020-09-05 14:46:31 +00:00
|
|
|
w->corner_radius = 0;
|
|
|
|
log_debug("Not rounding corners for window %#010x", w->base.id);
|
|
|
|
} else {
|
2023-03-29 20:18:41 +00:00
|
|
|
if (radius_override) {
|
|
|
|
w->corner_radius = (int)(long)radius_override;
|
|
|
|
} else {
|
2024-03-24 02:02:14 +00:00
|
|
|
w->corner_radius = options->corner_radius;
|
2023-03-29 20:18:41 +00:00
|
|
|
}
|
|
|
|
|
2020-09-05 14:46:31 +00:00
|
|
|
log_debug("Rounding corners for window %#010x", w->base.id);
|
2020-04-02 05:56:42 +00:00
|
|
|
// Initialize the border color to an invalid value
|
|
|
|
w->border_col[0] = w->border_col[1] = w->border_col[2] =
|
|
|
|
w->border_col[3] = -1.0F;
|
2020-09-05 14:46:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-12 16:34:51 +00:00
|
|
|
/**
|
|
|
|
* Determine custom window shader to use for a window.
|
|
|
|
*/
|
|
|
|
static void win_determine_fg_shader(session_t *ps, struct managed_win *w) {
|
|
|
|
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
auto options = session_get_options(ps);
|
|
|
|
auto c2_state = session_get_c2(ps);
|
|
|
|
auto shader_new = options->window_shader_fg;
|
2020-10-12 16:34:51 +00:00
|
|
|
void *val = NULL;
|
2024-03-24 02:02:14 +00:00
|
|
|
if (c2_match(c2_state, w, options->window_shader_fg_rules, &val)) {
|
2020-10-12 16:34:51 +00:00
|
|
|
shader_new = val;
|
|
|
|
}
|
2024-03-24 02:02:14 +00:00
|
|
|
win_set_fg_shader(ps, w, session_get_shader_info(ps, shader_new));
|
2020-10-12 16:34:51 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 18:17:26 +00:00
|
|
|
/**
|
|
|
|
* Update window opacity according to opacity rules.
|
|
|
|
*/
|
2019-04-17 22:14:45 +00:00
|
|
|
void win_update_opacity_rule(session_t *ps, struct managed_win *w) {
|
2019-07-26 21:28:53 +00:00
|
|
|
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
2019-07-26 21:28:53 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
double opacity = 1.0;
|
|
|
|
bool is_set = false;
|
|
|
|
void *val = NULL;
|
2024-03-24 02:02:14 +00:00
|
|
|
if (c2_match(session_get_c2(ps), w, session_get_options(ps)->opacity_rules, &val)) {
|
2019-03-10 12:34:37 +00:00
|
|
|
opacity = ((double)(long)val) / 100.0;
|
|
|
|
is_set = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->opacity_set = opacity;
|
|
|
|
w->opacity_is_set = is_set;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to be called on window data changes.
|
2019-02-09 15:50:40 +00:00
|
|
|
*
|
2020-08-30 14:25:58 +00:00
|
|
|
* TODO(yshui) need better name
|
2018-09-06 18:17:26 +00:00
|
|
|
*/
|
2019-04-17 22:14:45 +00:00
|
|
|
void win_on_factor_change(session_t *ps, struct managed_win *w) {
|
2020-04-10 03:19:53 +00:00
|
|
|
log_debug("Window %#010x (%s) factor change", w->base.id, w->name);
|
2024-03-24 02:02:14 +00:00
|
|
|
|
|
|
|
auto c2_state = session_get_c2(ps);
|
|
|
|
auto c = session_get_x_connection(ps);
|
|
|
|
auto options = session_get_options(ps);
|
|
|
|
c2_window_state_update(c2_state, &w->c2_state, c->c, w->client_win, w->base.id);
|
2024-02-14 18:54:04 +00:00
|
|
|
// Focus and is_fullscreen needs to be updated first, as other rules might depend
|
|
|
|
// on the focused state of the window
|
2020-03-17 19:50:48 +00:00
|
|
|
win_update_focused(ps, w);
|
2024-02-14 18:54:04 +00:00
|
|
|
win_update_is_fullscreen(ps, w);
|
2020-03-17 19:50:48 +00:00
|
|
|
|
2020-03-17 19:36:28 +00:00
|
|
|
win_determine_shadow(ps, w);
|
2021-07-09 17:17:23 +00:00
|
|
|
win_determine_clip_shadow_above(ps, w);
|
2020-03-17 19:36:28 +00:00
|
|
|
win_determine_invert_color(ps, w);
|
|
|
|
win_determine_blur_background(ps, w);
|
2020-09-05 14:46:31 +00:00
|
|
|
win_determine_rounded_corners(ps, w);
|
2020-10-12 16:34:51 +00:00
|
|
|
win_determine_fg_shader(ps, w);
|
2020-04-10 03:19:53 +00:00
|
|
|
w->mode = win_calc_mode(w);
|
|
|
|
log_debug("Window mode changed to %d", w->mode);
|
2020-03-17 19:36:28 +00:00
|
|
|
win_update_opacity_rule(ps, w);
|
2020-10-23 01:08:34 +00:00
|
|
|
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
|
2024-03-24 02:02:14 +00:00
|
|
|
w->paint_excluded = c2_match(c2_state, w, options->paint_blacklist, NULL);
|
2020-10-23 01:08:34 +00:00
|
|
|
}
|
|
|
|
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
|
2019-03-10 12:34:37 +00:00
|
|
|
w->unredir_if_possible_excluded =
|
2024-03-24 02:02:14 +00:00
|
|
|
c2_match(c2_state, w, options->unredir_if_possible_blacklist, NULL);
|
2020-10-23 01:08:34 +00:00
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
w->fade_excluded = c2_match(c2_state, w, options->fade_blacklist, NULL);
|
2020-03-17 19:50:48 +00:00
|
|
|
|
2021-12-01 22:51:57 +00:00
|
|
|
w->transparent_clipping_excluded =
|
2024-03-24 02:02:14 +00:00
|
|
|
c2_match(c2_state, w, options->transparent_clipping_blacklist, NULL);
|
2021-12-01 22:51:57 +00:00
|
|
|
|
2019-12-20 19:17:38 +00:00
|
|
|
win_update_opacity_target(ps, w);
|
2020-03-17 19:50:48 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
w->reg_ignore_valid = false;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update cache data in struct _win that depends on window size.
|
|
|
|
*/
|
2024-02-20 00:34:26 +00:00
|
|
|
void win_on_win_size_change(struct managed_win *w, int shadow_offset_x,
|
|
|
|
int shadow_offset_y, int shadow_radius) {
|
2024-01-21 19:26:20 +00:00
|
|
|
log_trace("Window %#010x (%s) size changed, was %dx%d, now %dx%d", w->base.id,
|
|
|
|
w->name, w->widthb, w->heightb, w->g.width + w->g.border_width * 2,
|
|
|
|
w->g.height + w->g.border_width * 2);
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
w->widthb = w->g.width + w->g.border_width * 2;
|
|
|
|
w->heightb = w->g.height + w->g.border_width * 2;
|
2024-02-19 23:11:21 +00:00
|
|
|
w->shadow_dx = shadow_offset_x;
|
|
|
|
w->shadow_dy = shadow_offset_y;
|
|
|
|
w->shadow_width = w->widthb + shadow_radius * 2;
|
|
|
|
w->shadow_height = w->heightb + shadow_radius * 2;
|
2019-04-20 04:26:20 +00:00
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// We don't handle property updates of non-visible windows until they are
|
|
|
|
// mapped.
|
2024-03-22 02:41:48 +00:00
|
|
|
assert(w->state == WSTATE_MAPPED);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update window type.
|
|
|
|
*/
|
2024-02-20 00:34:26 +00:00
|
|
|
bool win_update_wintype(struct x_connection *c, struct atom *atoms, struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
const wintype_t wtype_old = w->window_type;
|
|
|
|
|
|
|
|
// Detect window type here
|
2024-02-19 17:34:07 +00:00
|
|
|
w->window_type = wid_get_prop_wintype(c, atoms, w->client_win);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take
|
|
|
|
// override-redirect windows or windows without WM_TRANSIENT_FOR as
|
|
|
|
// _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG.
|
|
|
|
if (WINTYPE_UNKNOWN == w->window_type) {
|
|
|
|
if (w->a.override_redirect ||
|
2024-02-19 17:34:07 +00:00
|
|
|
!wid_has_prop(c->c, w->client_win, atoms->aWM_TRANSIENT_FOR)) {
|
2019-03-10 12:34:37 +00:00
|
|
|
w->window_type = WINTYPE_NORMAL;
|
2023-06-29 04:39:36 +00:00
|
|
|
} else {
|
2019-03-10 12:34:37 +00:00
|
|
|
w->window_type = WINTYPE_DIALOG;
|
2023-06-29 04:39:36 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2024-02-19 17:34:07 +00:00
|
|
|
return w->window_type != wtype_old;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mark a window as the client window of another.
|
|
|
|
*
|
|
|
|
* @param ps current session
|
|
|
|
* @param w struct _win of the parent window
|
|
|
|
* @param client window ID of the client window
|
|
|
|
*/
|
2024-02-19 22:56:07 +00:00
|
|
|
static void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client) {
|
2024-03-24 02:02:14 +00:00
|
|
|
auto options = session_get_options(ps);
|
|
|
|
auto c = session_get_x_connection(ps);
|
|
|
|
auto atoms = session_get_atoms(ps);
|
2019-03-10 12:34:37 +00:00
|
|
|
w->client_win = client;
|
|
|
|
|
|
|
|
// If the window isn't mapped yet, stop here, as the function will be
|
|
|
|
// called in map_win()
|
2020-10-23 01:08:34 +00:00
|
|
|
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
2020-10-23 01:08:34 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
auto e = xcb_request_check(
|
2024-03-24 02:02:14 +00:00
|
|
|
c->c, xcb_change_window_attributes_checked(
|
|
|
|
c->c, client, XCB_CW_EVENT_MASK,
|
|
|
|
(const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_CLIENT)}));
|
2019-03-10 12:34:37 +00:00
|
|
|
if (e) {
|
|
|
|
log_error("Failed to change event mask of window %#010x", client);
|
|
|
|
free(e);
|
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
win_update_wintype(c, atoms, w);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Get frame widths. The window is in damaged area already.
|
2024-03-24 02:02:14 +00:00
|
|
|
win_update_frame_extents(c, atoms, w, client, options->frame_opacity);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Get window group
|
2024-03-24 02:02:14 +00:00
|
|
|
if (options->track_leader) {
|
|
|
|
auto new_leader = win_get_leader_property(c, atoms, w->client_win,
|
|
|
|
options->detect_transient,
|
|
|
|
options->detect_client_leader);
|
2024-02-19 18:11:42 +00:00
|
|
|
if (w->leader != new_leader) {
|
|
|
|
win_set_leader(ps, w, new_leader);
|
|
|
|
}
|
2020-10-23 01:08:34 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Get window name and class if we are tracking them
|
2024-03-24 02:02:14 +00:00
|
|
|
win_update_name(c, atoms, w);
|
|
|
|
win_update_class(c, atoms, w);
|
|
|
|
win_update_role(c, atoms, w);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Update everything related to conditions
|
|
|
|
win_on_factor_change(ps, w);
|
|
|
|
|
2019-07-13 11:29:39 +00:00
|
|
|
auto r = xcb_get_window_attributes_reply(
|
2024-03-24 02:02:14 +00:00
|
|
|
c->c, xcb_get_window_attributes(c->c, w->client_win), &e);
|
2019-07-13 11:29:39 +00:00
|
|
|
if (!r) {
|
2020-04-11 15:00:36 +00:00
|
|
|
log_error_x_error(e, "Failed to get client window attributes");
|
2019-07-13 11:29:39 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
w->client_pictfmt = x_get_pictform_for_visual(c, r->visual);
|
2019-07-13 11:29:39 +00:00
|
|
|
free(r);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unmark current client window of a window.
|
|
|
|
*
|
|
|
|
* @param ps current session
|
|
|
|
* @param w struct _win of the parent window
|
|
|
|
*/
|
2019-04-17 22:14:45 +00:00
|
|
|
void win_unmark_client(session_t *ps, struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
xcb_window_t client = w->client_win;
|
2020-04-10 02:26:13 +00:00
|
|
|
log_debug("Detaching client window %#010x from frame %#010x (%s)", client,
|
|
|
|
w->base.id, w->name);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
w->client_win = XCB_NONE;
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Recheck event mask
|
|
|
|
xcb_change_window_attributes(
|
2024-03-24 02:02:14 +00:00
|
|
|
session_get_x_connection(ps)->c, client, XCB_CW_EVENT_MASK,
|
2019-03-10 12:34:37 +00:00
|
|
|
(const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)});
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2020-04-06 19:24:16 +00:00
|
|
|
/**
|
|
|
|
* Look for the client window of a particular window.
|
|
|
|
*/
|
2024-02-19 22:56:07 +00:00
|
|
|
static xcb_window_t
|
|
|
|
find_client_win(struct x_connection *c, struct atom *atoms, xcb_window_t w) {
|
|
|
|
if (wid_has_prop(c->c, w, atoms->aWM_STATE)) {
|
2020-04-06 19:24:16 +00:00
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
xcb_query_tree_reply_t *reply =
|
2024-02-19 22:56:07 +00:00
|
|
|
xcb_query_tree_reply(c->c, xcb_query_tree(c->c, w), NULL);
|
2020-11-27 05:34:54 +00:00
|
|
|
if (!reply) {
|
2020-04-06 19:24:16 +00:00
|
|
|
return 0;
|
2020-11-27 05:34:54 +00:00
|
|
|
}
|
2020-04-06 19:24:16 +00:00
|
|
|
|
|
|
|
xcb_window_t *children = xcb_query_tree_children(reply);
|
|
|
|
int nchildren = xcb_query_tree_children_length(reply);
|
|
|
|
int i;
|
|
|
|
xcb_window_t ret = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < nchildren; ++i) {
|
2024-02-19 22:56:07 +00:00
|
|
|
ret = find_client_win(c, atoms, children[i]);
|
|
|
|
if (ret) {
|
2020-04-06 19:24:16 +00:00
|
|
|
break;
|
2020-11-27 05:34:54 +00:00
|
|
|
}
|
2020-04-06 19:24:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
free(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-09-06 18:17:26 +00:00
|
|
|
/**
|
2024-02-19 22:56:07 +00:00
|
|
|
* Get client window of a window.
|
2018-09-06 18:17:26 +00:00
|
|
|
*
|
|
|
|
* @param ps current session
|
|
|
|
* @param w struct _win of the parent window
|
|
|
|
*/
|
2024-02-20 00:34:26 +00:00
|
|
|
xcb_window_t win_get_client_window(struct x_connection *c, struct atom *atoms,
|
|
|
|
const struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// Always recursively look for a window with WM_STATE, as Fluxbox
|
|
|
|
// sets override-redirect flags on all frame windows.
|
2024-02-19 22:56:07 +00:00
|
|
|
xcb_window_t cw = find_client_win(c, atoms, w->base.id);
|
2019-04-17 22:14:45 +00:00
|
|
|
if (cw) {
|
2020-04-10 02:26:13 +00:00
|
|
|
log_debug("(%#010x): client %#010x", w->base.id, cw);
|
2024-02-19 22:56:07 +00:00
|
|
|
} else {
|
|
|
|
// Set a window's client window to itself if we couldn't find a
|
|
|
|
// client window
|
2019-04-17 22:14:45 +00:00
|
|
|
cw = w->base.id;
|
2020-04-10 02:26:13 +00:00
|
|
|
log_debug("(%#010x): client self (%s)", w->base.id,
|
2024-02-19 22:47:28 +00:00
|
|
|
(w->a.override_redirect ? "override-redirected" : "wmwin"));
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2024-02-19 22:56:07 +00:00
|
|
|
return cw;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2019-02-09 15:50:40 +00:00
|
|
|
/**
|
|
|
|
* Free all resources in a <code>struct _win</code>.
|
|
|
|
*/
|
2019-04-17 22:14:45 +00:00
|
|
|
void free_win_res(session_t *ps, struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// No need to call backend release_image here because
|
|
|
|
// finish_unmap_win should've done that for us.
|
|
|
|
// XXX unless we are called by session_destroy
|
|
|
|
// assert(w->win_data == NULL);
|
|
|
|
free_win_res_glx(ps, w);
|
|
|
|
free_paint(ps, &w->paint);
|
|
|
|
free_paint(ps, &w->shadow_paint);
|
|
|
|
// Above should be done during unmapping
|
|
|
|
// Except when we are called by session_destroy
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
auto c = session_get_x_connection(ps);
|
2019-03-10 12:34:37 +00:00
|
|
|
pixman_region32_fini(&w->bounding_shape);
|
|
|
|
// BadDamage may be thrown if the window is destroyed
|
2024-03-24 02:02:14 +00:00
|
|
|
set_ignore_cookie(c, xcb_damage_destroy(c->c, w->damage));
|
2019-03-10 12:34:37 +00:00
|
|
|
rc_region_unref(&w->reg_ignore);
|
|
|
|
free(w->name);
|
|
|
|
free(w->class_instance);
|
|
|
|
free(w->class_general);
|
|
|
|
free(w->role);
|
2020-10-22 14:48:11 +00:00
|
|
|
|
|
|
|
free(w->stale_props);
|
|
|
|
w->stale_props = NULL;
|
|
|
|
w->stale_props_capacity = 0;
|
2024-03-24 02:02:14 +00:00
|
|
|
c2_window_state_destroy(session_get_c2(ps), &w->c2_state);
|
2019-04-17 22:14:45 +00:00
|
|
|
}
|
|
|
|
|
2024-03-24 02:12:25 +00:00
|
|
|
/// Query the Xorg for information about window `win`, and return that
|
|
|
|
/// information in a new managed_win object. However, if the window does
|
|
|
|
/// not need to be managed, the original `win` object is returned.
|
|
|
|
struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct win *w) {
|
2019-04-17 22:14:45 +00:00
|
|
|
static const struct managed_win win_def = {
|
2019-03-10 12:34:37 +00:00
|
|
|
// No need to initialize. (or, you can think that
|
|
|
|
// they are initialized right here).
|
|
|
|
// The following ones are updated during paint or paint preprocess
|
|
|
|
.shadow_opacity = 0.0,
|
|
|
|
.to_paint = false,
|
|
|
|
.frame_opacity = 1.0,
|
|
|
|
.dim = false,
|
|
|
|
.invert_color = false,
|
|
|
|
.blur_background = false,
|
|
|
|
.reg_ignore = NULL,
|
|
|
|
// The following ones are updated for other reasons
|
|
|
|
.pixmap_damaged = false, // updated by damage events
|
|
|
|
.state = WSTATE_UNMAPPED, // updated by window state changes
|
|
|
|
.in_openclose = true, // set to false after first map is done,
|
|
|
|
// true here because window is just created
|
|
|
|
.reg_ignore_valid = false, // set to true when damaged
|
2024-02-10 16:48:37 +00:00
|
|
|
.flags = WIN_FLAGS_PIXMAP_NONE, // updated by
|
2022-08-25 16:59:19 +00:00
|
|
|
// property/attributes/etc
|
2020-02-07 21:35:34 +00:00
|
|
|
// change
|
2020-10-22 14:48:11 +00:00
|
|
|
.stale_props = NULL,
|
|
|
|
.stale_props_capacity = 0,
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Runtime variables, updated by dbus
|
|
|
|
.fade_force = UNSET,
|
|
|
|
.shadow_force = UNSET,
|
|
|
|
.focused_force = UNSET,
|
|
|
|
.invert_color_force = UNSET,
|
|
|
|
|
|
|
|
// Initialized in this function
|
|
|
|
.a = {0},
|
|
|
|
.pictfmt = NULL,
|
2019-07-13 11:29:39 +00:00
|
|
|
.client_pictfmt = NULL,
|
2019-03-10 12:34:37 +00:00
|
|
|
.widthb = 0,
|
|
|
|
.heightb = 0,
|
|
|
|
.shadow_dx = 0,
|
|
|
|
.shadow_dy = 0,
|
|
|
|
.shadow_width = 0,
|
|
|
|
.shadow_height = 0,
|
|
|
|
.damage = XCB_NONE,
|
|
|
|
|
|
|
|
// Not initialized until mapped, this variables
|
|
|
|
// have no meaning or have no use until the window
|
|
|
|
// is mapped
|
|
|
|
.win_image = NULL,
|
|
|
|
.shadow_image = NULL,
|
2022-08-24 06:35:49 +00:00
|
|
|
.mask_image = NULL,
|
2019-03-10 12:34:37 +00:00
|
|
|
.prev_trans = NULL,
|
|
|
|
.shadow = false,
|
2021-07-09 17:17:23 +00:00
|
|
|
.clip_shadow_above = false,
|
2020-10-12 16:34:51 +00:00
|
|
|
.fg_shader = NULL,
|
2023-01-13 11:27:45 +00:00
|
|
|
.randr_monitor = -1,
|
2019-03-10 12:34:37 +00:00
|
|
|
.mode = WMODE_TRANS,
|
|
|
|
.ever_damaged = false,
|
|
|
|
.client_win = XCB_NONE,
|
|
|
|
.leader = XCB_NONE,
|
|
|
|
.cache_leader = XCB_NONE,
|
|
|
|
.window_type = WINTYPE_UNKNOWN,
|
|
|
|
.focused = false,
|
|
|
|
.has_opacity_prop = false,
|
|
|
|
.opacity_prop = OPAQUE,
|
|
|
|
.opacity_is_set = false,
|
|
|
|
.opacity_set = 1,
|
|
|
|
.frame_extents = MARGIN_INIT, // in win_mark_client
|
|
|
|
.bounding_shaped = false,
|
|
|
|
.bounding_shape = {0},
|
|
|
|
.rounded_corners = false,
|
|
|
|
.paint_excluded = false,
|
2020-10-23 01:08:34 +00:00
|
|
|
.fade_excluded = false,
|
2021-12-01 22:51:57 +00:00
|
|
|
.transparent_clipping_excluded = false,
|
2019-03-10 12:34:37 +00:00
|
|
|
.unredir_if_possible_excluded = false,
|
|
|
|
.prop_shadow = -1,
|
|
|
|
// following 4 are set in win_mark_client
|
|
|
|
.name = NULL,
|
|
|
|
.class_instance = NULL,
|
|
|
|
.class_general = NULL,
|
|
|
|
.role = NULL,
|
|
|
|
|
|
|
|
// Initialized during paint
|
|
|
|
.paint = PAINT_INIT,
|
|
|
|
.shadow_paint = PAINT_INIT,
|
2020-09-30 15:39:47 +00:00
|
|
|
|
|
|
|
.corner_radius = 0,
|
2019-03-10 12:34:37 +00:00
|
|
|
};
|
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
assert(!w->destroyed);
|
|
|
|
assert(w->is_new);
|
|
|
|
|
2019-04-19 20:10:38 +00:00
|
|
|
w->is_new = false;
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Reject overlay window and already added windows
|
2024-03-24 02:02:14 +00:00
|
|
|
if (w->id == session_get_overlay(ps)) {
|
2019-04-19 20:10:38 +00:00
|
|
|
return w;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
auto duplicated_win = find_managed_win(ps, w->id);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (duplicated_win) {
|
2022-08-25 16:59:19 +00:00
|
|
|
log_debug("Window %#010x (recorded name: %s) added multiple "
|
|
|
|
"times",
|
|
|
|
w->id, duplicated_win->name);
|
2019-04-19 20:10:38 +00:00
|
|
|
return &duplicated_win->base;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
auto c = session_get_x_connection(ps);
|
2019-04-27 12:36:42 +00:00
|
|
|
log_debug("Managing window %#010x", w->id);
|
2024-03-24 02:02:14 +00:00
|
|
|
xcb_get_window_attributes_cookie_t acookie = xcb_get_window_attributes(c->c, w->id);
|
2019-03-10 12:34:37 +00:00
|
|
|
xcb_get_window_attributes_reply_t *a =
|
2024-03-24 02:02:14 +00:00
|
|
|
xcb_get_window_attributes_reply(c->c, acookie, NULL);
|
2019-03-16 23:57:47 +00:00
|
|
|
if (!a || a->map_state == XCB_MAP_STATE_UNVIEWABLE) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// Failed to get window attributes or geometry probably means
|
|
|
|
// the window is gone already. Unviewable means the window is
|
|
|
|
// already reparented elsewhere.
|
2022-08-25 16:59:19 +00:00
|
|
|
// BTW, we don't care about Input Only windows, except for
|
|
|
|
// stacking proposes, so we need to keep track of them still.
|
2019-03-10 12:34:37 +00:00
|
|
|
free(a);
|
2019-04-19 20:10:38 +00:00
|
|
|
return w;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
if (a->_class == XCB_WINDOW_CLASS_INPUT_ONLY) {
|
2022-08-25 16:59:19 +00:00
|
|
|
// No need to manage this window, but we still keep it on the
|
|
|
|
// window stack
|
2019-04-17 22:14:45 +00:00
|
|
|
w->managed = false;
|
2019-09-20 08:13:07 +00:00
|
|
|
free(a);
|
2019-04-19 20:10:38 +00:00
|
|
|
return w;
|
2019-04-17 22:14:45 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Allocate and initialize the new win structure
|
2019-09-20 00:54:14 +00:00
|
|
|
auto new_internal = cmalloc(struct managed_win_internal);
|
|
|
|
auto new = (struct managed_win *)new_internal;
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Fill structure
|
|
|
|
// We only need to initialize the part that are not initialized
|
|
|
|
// by map_win
|
|
|
|
*new = win_def;
|
2019-04-17 22:14:45 +00:00
|
|
|
new->base = *w;
|
|
|
|
new->base.managed = true;
|
2019-03-10 12:34:37 +00:00
|
|
|
new->a = *a;
|
2024-03-19 16:44:02 +00:00
|
|
|
new->opacity = animatable_new(0, linear_interpolator, NULL);
|
|
|
|
new->blur_opacity = animatable_new(0, linear_interpolator, NULL);
|
2019-03-10 12:34:37 +00:00
|
|
|
pixman_region32_init(&new->bounding_shape);
|
|
|
|
|
|
|
|
free(a);
|
|
|
|
|
2020-11-27 05:34:54 +00:00
|
|
|
xcb_generic_error_t *e;
|
2024-03-24 02:02:14 +00:00
|
|
|
auto g = xcb_get_geometry_reply(c->c, xcb_get_geometry(c->c, w->id), &e);
|
2020-11-27 05:34:54 +00:00
|
|
|
if (!g) {
|
|
|
|
log_error_x_error(e, "Failed to get geometry of window %#010x", w->id);
|
|
|
|
free(e);
|
|
|
|
free(new);
|
|
|
|
return w;
|
|
|
|
}
|
2020-12-03 23:53:10 +00:00
|
|
|
new->pending_g = (struct win_geometry){
|
|
|
|
.x = g->x,
|
|
|
|
.y = g->y,
|
|
|
|
.width = g->width,
|
|
|
|
.height = g->height,
|
|
|
|
.border_width = g->border_width,
|
|
|
|
};
|
|
|
|
|
2020-11-27 05:34:54 +00:00
|
|
|
free(g);
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Create Damage for window (if not Input Only)
|
2024-03-24 02:02:14 +00:00
|
|
|
new->damage = x_new_id(c);
|
2020-11-27 05:34:54 +00:00
|
|
|
e = xcb_request_check(
|
2024-03-24 02:02:14 +00:00
|
|
|
c->c, xcb_damage_create_checked(c->c, new->damage, w->id,
|
|
|
|
XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY));
|
2019-04-17 22:14:45 +00:00
|
|
|
if (e) {
|
2020-11-27 05:34:54 +00:00
|
|
|
log_error_x_error(e, "Failed to create damage");
|
2019-04-17 22:14:45 +00:00
|
|
|
free(e);
|
|
|
|
free(new);
|
2019-04-19 20:10:38 +00:00
|
|
|
return w;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2020-11-27 05:34:54 +00:00
|
|
|
// Set window event mask
|
|
|
|
xcb_change_window_attributes(
|
2024-03-24 02:02:14 +00:00
|
|
|
c->c, new->base.id, XCB_CW_EVENT_MASK,
|
2020-11-27 05:34:54 +00:00
|
|
|
(const uint32_t[]){determine_evmask(ps, new->base.id, WIN_EVMODE_FRAME)});
|
|
|
|
|
|
|
|
// Get notification when the shape of a window changes
|
2024-03-24 02:02:14 +00:00
|
|
|
if (session_has_shape_extension(ps)) {
|
|
|
|
xcb_shape_select_input(c->c, new->base.id, 1);
|
2020-11-27 05:34:54 +00:00
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
new->pictfmt = x_get_pictform_for_visual(c, new->a.visual);
|
2019-07-13 11:29:39 +00:00
|
|
|
new->client_pictfmt = NULL;
|
2019-04-17 22:14:45 +00:00
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// Set all the stale flags on this new window, so it's properties will get
|
|
|
|
// updated when it's mapped
|
2020-11-27 05:34:54 +00:00
|
|
|
win_set_flags(new, WIN_FLAGS_CLIENT_STALE | WIN_FLAGS_SIZE_STALE |
|
|
|
|
WIN_FLAGS_POSITION_STALE | WIN_FLAGS_PROPERTY_STALE |
|
|
|
|
WIN_FLAGS_FACTOR_CHANGED);
|
2024-03-24 02:02:14 +00:00
|
|
|
|
|
|
|
auto atoms = session_get_atoms(ps);
|
|
|
|
auto c2_state = session_get_c2(ps);
|
2020-11-27 05:34:54 +00:00
|
|
|
xcb_atom_t init_stale_props[] = {
|
2024-03-24 02:02:14 +00:00
|
|
|
atoms->a_NET_WM_WINDOW_TYPE, atoms->a_NET_WM_WINDOW_OPACITY,
|
|
|
|
atoms->a_NET_FRAME_EXTENTS, atoms->aWM_NAME,
|
|
|
|
atoms->a_NET_WM_NAME, atoms->aWM_CLASS,
|
|
|
|
atoms->aWM_WINDOW_ROLE, atoms->a_COMPTON_SHADOW,
|
|
|
|
atoms->aWM_CLIENT_LEADER, atoms->aWM_TRANSIENT_FOR,
|
|
|
|
atoms->a_NET_WM_STATE,
|
2020-11-27 05:34:54 +00:00
|
|
|
};
|
|
|
|
win_set_properties_stale(new, init_stale_props, ARR_SIZE(init_stale_props));
|
2024-03-24 02:02:14 +00:00
|
|
|
c2_window_state_init(c2_state, &new->c2_state);
|
2020-11-27 05:34:54 +00:00
|
|
|
|
2019-04-19 20:10:38 +00:00
|
|
|
return &new->base;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update leader of a window.
|
|
|
|
*/
|
2024-02-19 18:11:42 +00:00
|
|
|
static xcb_window_t
|
|
|
|
win_get_leader_property(struct x_connection *c, struct atom *atoms, xcb_window_t wid,
|
|
|
|
bool detect_transient, bool detect_client_leader) {
|
2019-03-10 12:34:37 +00:00
|
|
|
xcb_window_t leader = XCB_NONE;
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Read the leader properties
|
2024-02-19 18:11:42 +00:00
|
|
|
if (detect_transient) {
|
|
|
|
leader = wid_get_prop_window(c, wid, atoms->aWM_TRANSIENT_FOR);
|
2020-10-23 01:08:34 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2024-02-19 18:11:42 +00:00
|
|
|
if (detect_client_leader && leader == XCB_NONE) {
|
|
|
|
leader = wid_get_prop_window(c, wid, atoms->aWM_CLIENT_LEADER);
|
2020-10-23 01:08:34 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2024-02-19 18:11:42 +00:00
|
|
|
log_trace("window %#010x: leader %#010x", wid, leader);
|
|
|
|
return leader;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal function of win_get_leader().
|
|
|
|
*/
|
2019-08-10 00:44:27 +00:00
|
|
|
static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int recursions) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// Rebuild the cache if needed
|
|
|
|
if (!w->cache_leader && (w->client_win || w->leader)) {
|
|
|
|
// Leader defaults to client window
|
2023-06-29 04:39:36 +00:00
|
|
|
if (!(w->cache_leader = w->leader)) {
|
2019-03-10 12:34:37 +00:00
|
|
|
w->cache_leader = w->client_win;
|
2023-06-29 04:39:36 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// If the leader of this window isn't itself, look for its
|
|
|
|
// ancestors
|
2019-03-10 12:34:37 +00:00
|
|
|
if (w->cache_leader && w->cache_leader != w->client_win) {
|
2024-03-24 02:02:14 +00:00
|
|
|
auto wp = session_find_toplevel(ps, w->cache_leader);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (wp) {
|
|
|
|
// Dead loop?
|
2023-06-29 04:39:36 +00:00
|
|
|
if (recursions > WIN_GET_LEADER_MAX_RECURSION) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return XCB_NONE;
|
2023-06-29 04:39:36 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
w->cache_leader = win_get_leader_raw(ps, wp, recursions + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return w->cache_leader;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the <code>WM_CLASS</code> of a window and update its
|
|
|
|
* <code>win</code> structure.
|
|
|
|
*/
|
2024-02-20 00:34:26 +00:00
|
|
|
bool win_update_class(struct x_connection *c, struct atom *atoms, struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
char **strlst = NULL;
|
|
|
|
int nstr = 0;
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Can't do anything if there's no client window
|
2023-06-29 04:39:36 +00:00
|
|
|
if (!w->client_win) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return false;
|
2023-06-29 04:39:36 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Free and reset old strings
|
|
|
|
free(w->class_instance);
|
|
|
|
free(w->class_general);
|
|
|
|
w->class_instance = NULL;
|
|
|
|
w->class_general = NULL;
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Retrieve the property string list
|
2024-02-19 17:17:35 +00:00
|
|
|
if (!wid_get_text_prop(c, atoms, w->client_win, atoms->aWM_CLASS, &strlst, &nstr)) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return false;
|
2020-10-22 00:39:51 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Copy the strings if successful
|
|
|
|
w->class_instance = strdup(strlst[0]);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2020-10-22 00:39:51 +00:00
|
|
|
if (nstr > 1) {
|
2019-03-10 12:34:37 +00:00
|
|
|
w->class_general = strdup(strlst[1]);
|
2020-10-22 00:39:51 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2020-10-22 00:39:51 +00:00
|
|
|
free(strlst);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
log_trace("(%#010x): client = %#010x, "
|
|
|
|
"instance = \"%s\", general = \"%s\"",
|
2019-04-17 22:14:45 +00:00
|
|
|
w->base.id, w->client_win, w->class_instance, w->class_general);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
return true;
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle window focus change.
|
|
|
|
*/
|
2019-04-17 22:14:45 +00:00
|
|
|
static void win_on_focus_change(session_t *ps, struct managed_win *w) {
|
2024-03-24 02:02:14 +00:00
|
|
|
auto options = session_get_options(ps);
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// If window grouping detection is enabled
|
2024-03-24 02:02:14 +00:00
|
|
|
if (options->track_leader) {
|
2019-03-10 12:34:37 +00:00
|
|
|
xcb_window_t leader = win_get_leader(ps, w);
|
2024-03-24 02:02:14 +00:00
|
|
|
xcb_window_t active_leader_old = session_get_active_leader(ps);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (win_is_focused_raw(w) && leader != active_leader_old) {
|
|
|
|
// If the window gets focused, replace the old active_leader
|
|
|
|
session_set_active_leader(ps, leader);
|
2020-03-17 19:50:48 +00:00
|
|
|
group_on_factor_change(ps, active_leader_old);
|
|
|
|
group_on_factor_change(ps, leader);
|
2024-03-24 02:02:14 +00:00
|
|
|
} else if (!win_is_focused_raw(w) && leader &&
|
|
|
|
leader == active_leader_old && !group_is_focused(ps, leader)) {
|
|
|
|
// If the group get unfocused, remove it from active_leader
|
|
|
|
session_set_active_leader(ps, XCB_NONE);
|
2020-03-17 19:50:48 +00:00
|
|
|
group_on_factor_change(ps, leader);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update everything related to conditions
|
|
|
|
win_on_factor_change(ps, w);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Send D-Bus signal
|
2024-03-24 02:02:14 +00:00
|
|
|
if (options->dbus) {
|
2024-02-15 00:07:31 +00:00
|
|
|
if (win_is_focused_raw(w)) {
|
2024-03-23 14:01:46 +00:00
|
|
|
cdbus_ev_win_focusin(session_get_cdbus(ps), &w->base);
|
2020-10-23 01:08:34 +00:00
|
|
|
} else {
|
2024-03-23 14:01:46 +00:00
|
|
|
cdbus_ev_win_focusout(session_get_cdbus(ps), &w->base);
|
2020-10-23 01:08:34 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set real focused state of a window.
|
|
|
|
*/
|
2019-05-24 22:13:24 +00:00
|
|
|
void win_set_focused(session_t *ps, struct managed_win *w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// Unmapped windows will have their focused state reset on map
|
2019-05-24 22:13:24 +00:00
|
|
|
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
2019-05-24 22:13:24 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2024-02-15 00:07:31 +00:00
|
|
|
if (w->is_ewmh_focused) {
|
2024-03-24 02:02:14 +00:00
|
|
|
assert(session_get_active_win(ps) == w);
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
2019-05-24 22:13:24 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
auto old_active_win = session_get_active_win(ps);
|
|
|
|
session_set_active_win(ps, w);
|
2024-02-15 00:07:31 +00:00
|
|
|
w->is_ewmh_focused = true;
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-05-24 22:34:30 +00:00
|
|
|
if (old_active_win) {
|
2024-02-15 00:07:31 +00:00
|
|
|
assert(old_active_win->is_ewmh_focused);
|
|
|
|
old_active_win->is_ewmh_focused = false;
|
2019-05-24 22:34:30 +00:00
|
|
|
win_on_focus_change(ps, old_active_win);
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
win_on_focus_change(ps, w);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a rectangular region a window (and possibly its shadow) occupies.
|
|
|
|
*
|
|
|
|
* Note w->shadow and shadow geometry must be correct before calling this
|
|
|
|
* function.
|
|
|
|
*/
|
2019-04-17 22:14:45 +00:00
|
|
|
void win_extents(const struct managed_win *w, region_t *res) {
|
2019-03-10 12:34:37 +00:00
|
|
|
pixman_region32_clear(res);
|
2019-03-30 09:07:21 +00:00
|
|
|
pixman_region32_union_rect(res, res, w->g.x, w->g.y, (uint)w->widthb, (uint)w->heightb);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-30 09:07:21 +00:00
|
|
|
if (w->shadow) {
|
|
|
|
assert(w->shadow_width >= 0 && w->shadow_height >= 0);
|
2019-03-10 12:34:37 +00:00
|
|
|
pixman_region32_union_rect(res, res, w->g.x + w->shadow_dx,
|
2019-03-30 09:07:21 +00:00
|
|
|
w->g.y + w->shadow_dy, (uint)w->shadow_width,
|
|
|
|
(uint)w->shadow_height);
|
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
gen_by_val(win_extents);
|
2018-12-22 00:33:40 +00:00
|
|
|
|
2019-03-16 20:14:54 +00:00
|
|
|
/**
|
|
|
|
* Update the out-dated bounding shape of a window.
|
|
|
|
*
|
|
|
|
* Mark the window shape as updated
|
|
|
|
*/
|
2024-02-20 00:34:26 +00:00
|
|
|
void win_update_bounding_shape(struct x_connection *c, struct managed_win *w,
|
|
|
|
bool shape_exists, bool detect_rounded_corners) {
|
2022-08-25 16:59:19 +00:00
|
|
|
// We don't handle property updates of non-visible windows until they are
|
|
|
|
// mapped.
|
2024-03-22 02:41:48 +00:00
|
|
|
assert(w->state == WSTATE_MAPPED);
|
2020-11-28 02:11:17 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
pixman_region32_clear(&w->bounding_shape);
|
|
|
|
// Start with the window rectangular region
|
|
|
|
win_get_region_local(w, &w->bounding_shape);
|
|
|
|
|
2024-02-19 23:16:12 +00:00
|
|
|
if (shape_exists) {
|
|
|
|
w->bounding_shaped = win_bounding_shaped(c, w->base.id);
|
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Only request for a bounding region if the window is shaped
|
|
|
|
// (while loop is used to avoid goto, not an actual loop)
|
|
|
|
while (w->bounding_shaped) {
|
|
|
|
/*
|
|
|
|
* if window doesn't exist anymore, this will generate an error
|
|
|
|
* as well as not generate a region.
|
|
|
|
*/
|
|
|
|
|
|
|
|
xcb_shape_get_rectangles_reply_t *r = xcb_shape_get_rectangles_reply(
|
2024-02-19 23:16:12 +00:00
|
|
|
c->c, xcb_shape_get_rectangles(c->c, w->base.id, XCB_SHAPE_SK_BOUNDING),
|
2023-06-29 04:39:36 +00:00
|
|
|
NULL);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2020-10-23 03:34:05 +00:00
|
|
|
if (!r) {
|
2019-03-10 12:34:37 +00:00
|
|
|
break;
|
2020-10-23 03:34:05 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
xcb_rectangle_t *xrects = xcb_shape_get_rectangles_rectangles(r);
|
|
|
|
int nrects = xcb_shape_get_rectangles_rectangles_length(r);
|
|
|
|
rect_t *rects = from_x_rects(nrects, xrects);
|
|
|
|
free(r);
|
|
|
|
|
|
|
|
region_t br;
|
|
|
|
pixman_region32_init_rects(&br, rects, nrects);
|
|
|
|
free(rects);
|
|
|
|
|
|
|
|
// Add border width because we are using a different origin.
|
|
|
|
// X thinks the top left of the inner window is the origin
|
2024-01-31 03:09:28 +00:00
|
|
|
// (for the bounding shape, although xcb_get_geometry thinks
|
2019-03-10 12:34:37 +00:00
|
|
|
// the outer top left (outer means outside of the window
|
|
|
|
// border) is the origin),
|
|
|
|
// We think the top left of the border is the origin
|
|
|
|
pixman_region32_translate(&br, w->g.border_width, w->g.border_width);
|
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// Intersect the bounding region we got with the window rectangle,
|
|
|
|
// to make sure the bounding region is not bigger than the window
|
2019-03-10 12:34:37 +00:00
|
|
|
// rectangle
|
|
|
|
pixman_region32_intersect(&w->bounding_shape, &w->bounding_shape, &br);
|
|
|
|
pixman_region32_fini(&br);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-02-19 23:16:12 +00:00
|
|
|
if (w->bounding_shaped && detect_rounded_corners) {
|
2019-03-10 12:34:37 +00:00
|
|
|
w->rounded_corners = win_has_rounded_corners(w);
|
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reread opacity property of a window.
|
|
|
|
*/
|
2024-02-19 22:09:01 +00:00
|
|
|
void win_update_opacity_prop(struct x_connection *c, struct atom *atoms,
|
|
|
|
struct managed_win *w, bool detect_client_opacity) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// get frame opacity first
|
2024-02-19 17:17:35 +00:00
|
|
|
w->has_opacity_prop =
|
2024-02-19 22:09:01 +00:00
|
|
|
wid_get_opacity_prop(c, atoms, w->base.id, OPAQUE, &w->opacity_prop);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2020-10-23 01:08:34 +00:00
|
|
|
if (w->has_opacity_prop) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// opacity found
|
|
|
|
return;
|
2020-10-23 01:08:34 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2024-02-19 22:09:01 +00:00
|
|
|
if (detect_client_opacity && w->client_win && w->base.id == w->client_win) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// checking client opacity not allowed
|
|
|
|
return;
|
2020-10-23 01:08:34 +00:00
|
|
|
}
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// get client opacity
|
|
|
|
w->has_opacity_prop =
|
2024-02-19 22:09:01 +00:00
|
|
|
wid_get_opacity_prop(c, atoms, w->client_win, OPAQUE, &w->opacity_prop);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve frame extents from a window.
|
|
|
|
*/
|
2024-02-19 22:48:44 +00:00
|
|
|
void win_update_frame_extents(struct x_connection *c, struct atom *atoms,
|
|
|
|
struct managed_win *w, xcb_window_t client,
|
|
|
|
double frame_opacity) {
|
|
|
|
winprop_t prop =
|
|
|
|
x_get_prop(c, client, atoms->a_NET_FRAME_EXTENTS, 4L, XCB_ATOM_CARDINAL, 32);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
if (prop.nitems == 4) {
|
2020-12-18 23:43:11 +00:00
|
|
|
int extents[4];
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
if (prop.c32[i] > (uint32_t)INT_MAX) {
|
|
|
|
log_warn("Your window manager sets a absurd "
|
2022-08-25 16:59:19 +00:00
|
|
|
"_NET_FRAME_EXTENTS value (%u), "
|
|
|
|
"ignoring it.",
|
2020-12-18 23:43:11 +00:00
|
|
|
prop.c32[i]);
|
|
|
|
memset(extents, 0, sizeof(extents));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
extents[i] = (int)prop.c32[i];
|
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
const bool changed = w->frame_extents.left != extents[0] ||
|
|
|
|
w->frame_extents.right != extents[1] ||
|
|
|
|
w->frame_extents.top != extents[2] ||
|
|
|
|
w->frame_extents.bottom != extents[3];
|
|
|
|
w->frame_extents.left = extents[0];
|
|
|
|
w->frame_extents.right = extents[1];
|
|
|
|
w->frame_extents.top = extents[2];
|
|
|
|
w->frame_extents.bottom = extents[3];
|
|
|
|
|
|
|
|
// If frame_opacity != 1, then frame of this window
|
|
|
|
// is not included in reg_ignore of underneath windows
|
2024-02-19 22:48:44 +00:00
|
|
|
if (frame_opacity == 1 && changed) {
|
2019-03-10 12:34:37 +00:00
|
|
|
w->reg_ignore_valid = false;
|
2020-10-23 01:08:34 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
log_trace("(%#010x): %d, %d, %d, %d", w->base.id, w->frame_extents.left,
|
2019-03-10 12:34:37 +00:00
|
|
|
w->frame_extents.right, w->frame_extents.top, w->frame_extents.bottom);
|
|
|
|
|
|
|
|
free_winprop(&prop);
|
2018-09-06 18:17:26 +00:00
|
|
|
}
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 03:56:00 +00:00
|
|
|
|
2018-11-03 22:38:18 +00:00
|
|
|
/**
|
|
|
|
* Stop listening for events on a particular window.
|
|
|
|
*/
|
2019-04-17 22:14:45 +00:00
|
|
|
void win_ev_stop(session_t *ps, const struct win *w) {
|
2024-03-24 02:02:14 +00:00
|
|
|
auto c = session_get_x_connection(ps)->c;
|
|
|
|
xcb_change_window_attributes(c, w->id, XCB_CW_EVENT_MASK, (const uint32_t[]){0});
|
2018-11-03 22:38:18 +00:00
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
if (!w->managed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto mw = (struct managed_win *)w;
|
|
|
|
if (mw->client_win) {
|
2024-03-24 02:02:14 +00:00
|
|
|
xcb_change_window_attributes(c, mw->client_win, XCB_CW_EVENT_MASK,
|
2019-03-10 12:34:37 +00:00
|
|
|
(const uint32_t[]){0});
|
|
|
|
}
|
2018-11-03 22:38:18 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (session_has_shape_extension(ps)) {
|
|
|
|
xcb_shape_select_input(c, w->id, 0);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2018-11-03 22:38:18 +00:00
|
|
|
}
|
2018-12-15 21:11:41 +00:00
|
|
|
|
2019-09-16 21:14:34 +00:00
|
|
|
/// Finish the destruction of a window (e.g. after fading has finished).
|
|
|
|
/// Frees `w`
|
2024-03-22 02:41:48 +00:00
|
|
|
void destroy_win_finish(session_t *ps, struct win *w) {
|
|
|
|
log_verbose("Trying to finish destroying (%#010x)", w->id);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
auto next_w = session_get_next_managed_win_in_stack(ps, &w->stack_neighbour);
|
2019-09-20 00:23:44 +00:00
|
|
|
list_remove(&w->stack_neighbour);
|
|
|
|
|
|
|
|
if (w->managed) {
|
|
|
|
auto mw = (struct managed_win *)w;
|
2024-03-24 02:02:14 +00:00
|
|
|
auto backend_data = session_get_backend_data(ps);
|
2024-03-22 02:41:48 +00:00
|
|
|
unmap_win_finish(ps, mw);
|
2019-09-20 00:23:44 +00:00
|
|
|
|
2024-02-10 16:48:37 +00:00
|
|
|
// Unmapping might preserve the shadow image, so free it here
|
2024-03-24 02:02:14 +00:00
|
|
|
win_release_shadow(backend_data, mw);
|
|
|
|
win_release_mask(backend_data, mw);
|
2020-11-27 05:34:54 +00:00
|
|
|
|
2019-09-20 00:23:44 +00:00
|
|
|
// Invalidate reg_ignore of windows below this one
|
2020-08-30 14:25:58 +00:00
|
|
|
// TODO(yshui) what if next_w is not mapped??
|
|
|
|
/* TODO(yshui) seriously figure out how reg_ignore behaves.
|
|
|
|
* I think if `w` is unmapped, and destroyed after
|
|
|
|
* paint happened at least once, w->reg_ignore_valid would
|
|
|
|
* be true, and there is no need to invalid w->next->reg_ignore
|
|
|
|
* when w is destroyed. */
|
2019-09-20 00:23:44 +00:00
|
|
|
if (next_w) {
|
|
|
|
rc_region_unref(&next_w->reg_ignore);
|
|
|
|
next_w->reg_ignore_valid = false;
|
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (mw == session_get_active_win(ps)) {
|
2022-08-25 16:59:19 +00:00
|
|
|
// Usually, the window cannot be the focused at
|
|
|
|
// destruction. FocusOut should be generated before the
|
|
|
|
// window is destroyed. We do this check just to be
|
|
|
|
// completely sure we don't have dangling references.
|
|
|
|
log_debug("window %#010x (%s) is destroyed while being "
|
|
|
|
"focused",
|
2019-09-20 00:23:44 +00:00
|
|
|
w->id, mw->name);
|
2024-03-24 02:02:14 +00:00
|
|
|
session_set_active_win(ps, NULL);
|
2019-09-20 00:23:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
free_win_res(ps, mw);
|
|
|
|
|
|
|
|
// Drop w from all prev_trans to avoid accessing freed memory in
|
|
|
|
// repair_win()
|
2020-08-30 14:25:58 +00:00
|
|
|
// TODO(yshui) there can only be one prev_trans pointing to w
|
2024-03-24 02:02:14 +00:00
|
|
|
win_stack_foreach_managed(w2, session_get_win_stack(ps)) {
|
2019-09-20 00:23:44 +00:00
|
|
|
if (mw == w2->prev_trans) {
|
|
|
|
w2->prev_trans = NULL;
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-20 00:23:44 +00:00
|
|
|
|
2019-04-03 19:48:46 +00:00
|
|
|
free(w);
|
2019-02-09 15:50:40 +00:00
|
|
|
}
|
2019-09-20 00:23:44 +00:00
|
|
|
/// Start destroying a window. Windows cannot always be destroyed immediately
|
|
|
|
/// because of fading and such.
|
2024-03-22 02:41:48 +00:00
|
|
|
void destroy_win_start(session_t *ps, struct win *w) {
|
2019-09-20 00:23:44 +00:00
|
|
|
auto mw = (struct managed_win *)w;
|
|
|
|
assert(w);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-09-20 00:23:44 +00:00
|
|
|
log_debug("Destroying %#010x \"%s\", managed = %d", w->id,
|
|
|
|
(w->managed ? mw->name : NULL), w->managed);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// Delete destroyed window from the hash table, even though the window
|
|
|
|
// might still be rendered for a while. We need to make sure future window
|
|
|
|
// with the same window id won't confuse us. Keep the window in the window
|
|
|
|
// stack if it's managed and mapped, since we might still need to render
|
|
|
|
// it (e.g. fading out). Window will be removed from the stack when it
|
|
|
|
// finishes destroying.
|
2024-03-24 02:02:14 +00:00
|
|
|
session_delete_win(ps, w);
|
2019-09-20 00:23:44 +00:00
|
|
|
|
|
|
|
if (w->managed) {
|
2024-03-22 02:41:48 +00:00
|
|
|
if (mw->state != WSTATE_UNMAPPED) {
|
|
|
|
// Only UNMAPPED state has window resources freed,
|
|
|
|
// otherwise we need to call unmap_win_finish to free
|
|
|
|
// them.
|
|
|
|
log_warn("Did X server not unmap window %#010x before destroying "
|
|
|
|
"it?",
|
|
|
|
w->id);
|
|
|
|
}
|
2020-08-31 22:07:28 +00:00
|
|
|
// Clear IMAGES_STALE flags since the window is destroyed: Clear
|
2022-08-25 16:59:19 +00:00
|
|
|
// PIXMAP_STALE as there is no pixmap available anymore, so STALE
|
|
|
|
// doesn't make sense.
|
|
|
|
// XXX Clear SHADOW_STALE as setting/clearing flags on a destroyed
|
|
|
|
// window doesn't work leading to an inconsistent state where the
|
|
|
|
// shadow is refreshed but the flags are stuck in STALE. Do this
|
|
|
|
// before changing the window state to destroying
|
2024-02-10 16:48:37 +00:00
|
|
|
win_clear_flags(mw, WIN_FLAGS_PIXMAP_STALE);
|
2020-04-06 19:52:32 +00:00
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// If size/shape/position information is stale,
|
|
|
|
// win_process_update_flags will update them and add the new
|
|
|
|
// window extents to damage. Since the window has been destroyed,
|
|
|
|
// we cannot get the complete information at this point, so we
|
|
|
|
// just add what we currently have to the damage.
|
2020-10-23 16:22:15 +00:00
|
|
|
if (win_check_flags_any(mw, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE)) {
|
|
|
|
add_damage_from_win(ps, mw);
|
|
|
|
}
|
|
|
|
|
2024-01-21 19:58:29 +00:00
|
|
|
if (win_check_flags_all(mw, WIN_FLAGS_CLIENT_STALE)) {
|
|
|
|
mw->client_win = mw->base.id;
|
|
|
|
log_debug("(%#010x): client self (%s)", mw->base.id,
|
2024-02-19 22:47:28 +00:00
|
|
|
(mw->a.override_redirect ? "override-redirected" : "wmwin"));
|
2024-01-21 19:58:29 +00:00
|
|
|
}
|
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// Clear some flags about stale window information. Because now
|
|
|
|
// the window is destroyed, we can't update them anyway.
|
2020-10-23 16:22:15 +00:00
|
|
|
win_clear_flags(mw, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE |
|
|
|
|
WIN_FLAGS_PROPERTY_STALE |
|
|
|
|
WIN_FLAGS_FACTOR_CHANGED | WIN_FLAGS_CLIENT_STALE);
|
2020-04-06 19:52:32 +00:00
|
|
|
|
2019-09-20 00:23:44 +00:00
|
|
|
// Update state flags of a managed window
|
2024-03-22 02:41:48 +00:00
|
|
|
mw->state = WSTATE_DESTROYED;
|
2019-09-20 00:23:44 +00:00
|
|
|
mw->a.map_state = XCB_MAP_STATE_UNMAPPED;
|
|
|
|
mw->in_openclose = true;
|
2024-03-22 02:41:48 +00:00
|
|
|
|
|
|
|
// We don't initiate animation here, because it should already have been
|
|
|
|
// started by unmap_win_start, because X automatically unmaps windows
|
|
|
|
// before destroying them. But we do need to stop animation if
|
|
|
|
// no_fading_destroyed_windows, or no_fading_openclose is enabled.
|
|
|
|
if (!win_should_fade(ps, mw)) {
|
|
|
|
win_skip_fading(mw);
|
|
|
|
}
|
2019-09-20 00:23:44 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-09-20 00:23:44 +00:00
|
|
|
// don't need win_ev_stop because the window is gone anyway
|
|
|
|
// Send D-Bus signal
|
2024-03-24 02:02:14 +00:00
|
|
|
if (session_get_options(ps)->dbus) {
|
2024-03-23 14:01:46 +00:00
|
|
|
cdbus_ev_win_destroyed(session_get_cdbus(ps), w);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (!session_is_redirected(ps) && w->managed) {
|
2019-09-20 00:23:44 +00:00
|
|
|
// Skip transition if we are not rendering
|
2024-03-22 02:41:48 +00:00
|
|
|
win_skip_fading(mw);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2019-09-20 00:23:44 +00:00
|
|
|
}
|
|
|
|
|
2019-09-20 00:41:56 +00:00
|
|
|
void unmap_win_start(session_t *ps, struct managed_win *w) {
|
2019-09-20 00:23:44 +00:00
|
|
|
assert(w);
|
|
|
|
assert(w->base.managed);
|
|
|
|
assert(w->a._class != XCB_WINDOW_CLASS_INPUT_ONLY);
|
|
|
|
|
|
|
|
log_debug("Unmapping %#010x \"%s\"", w->base.id, w->name);
|
|
|
|
|
2024-03-22 02:41:48 +00:00
|
|
|
assert(w->state != WSTATE_DESTROYED);
|
win: fix window not being rendered when it's mapped and damged while fading out
Consider these 2 cases:
* A window is mapped while fading out
* A window is mapped and damaged while fading out
From the perspective of map_win_start, these 2 cases look the same. The
window will has ever_damage = true in both cases. However, map_win_start
has to distinguish between these 2 cases, because at the end of
map_win_start, window in the first case cannot have ever_damage = true,
while window in the second case should have ever_damage = true.
Currently, map_win_start always clears ever_damage in both cases
(indirectly through win_skip_fading), which causes windows in the second
case to not be rendered when they should be.
This commit move clearing of ever_damage from unmap_win_finish to
unmap_win_start, so when map_win_start sees ever_damage = true, it's
always to second case. And to make sure windows which are fading out are
still rendered, we relax the ever_damage check in paint_preprocess to
also accept windows that are fading out. (see code comment for
explanation why this is fine)
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-12-04 03:06:30 +00:00
|
|
|
|
2024-03-22 02:41:48 +00:00
|
|
|
if (unlikely(w->state == WSTATE_UNMAPPED)) {
|
|
|
|
assert(win_check_flags_all(w, WIN_FLAGS_MAPPED));
|
|
|
|
// Window is mapped, but we hadn't had a chance to handle the MAPPED flag.
|
|
|
|
// Clear the pending map as this window is now unmapped
|
|
|
|
win_clear_flags(w, WIN_FLAGS_MAPPED);
|
2019-09-20 00:23:44 +00:00
|
|
|
return;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2019-05-24 22:29:26 +00:00
|
|
|
// Note we don't update focused window here. This will either be
|
|
|
|
// triggered by subsequence Focus{In, Out} event, or by recheck_focus
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
w->a.map_state = XCB_MAP_STATE_UNMAPPED;
|
2024-03-22 02:41:48 +00:00
|
|
|
w->state = WSTATE_UNMAPPED;
|
|
|
|
win_start_fade(ps, w, 0);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Send D-Bus signal
|
2024-03-24 02:02:14 +00:00
|
|
|
if (session_get_options(ps)->dbus) {
|
2024-03-23 14:01:46 +00:00
|
|
|
cdbus_ev_win_unmapped(session_get_cdbus(ps), &w->base);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2019-02-21 02:12:07 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (!session_is_redirected(ps) || !w->ever_damaged) {
|
2022-08-25 16:59:19 +00:00
|
|
|
// If we are not redirected, we skip fading because we aren't
|
|
|
|
// rendering anything anyway. If the window wasn't ever damaged,
|
|
|
|
// it shouldn't be painted either. But a fading out window is
|
|
|
|
// always painted, so we have to skip fading here.
|
2024-03-22 02:41:48 +00:00
|
|
|
win_skip_fading(w);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2018-12-15 21:11:41 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 02:12:07 +00:00
|
|
|
/// Skip the current in progress fading of window,
|
|
|
|
/// transition the window straight to its end state
|
2024-03-22 02:41:48 +00:00
|
|
|
void win_skip_fading(struct managed_win *w) {
|
|
|
|
if (w->number_of_animations == 0) {
|
|
|
|
return;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2019-09-19 16:23:48 +00:00
|
|
|
log_debug("Skipping fading process of window %#010x (%s)", w->base.id, w->name);
|
2024-03-19 16:44:02 +00:00
|
|
|
animatable_early_stop(&w->opacity);
|
|
|
|
animatable_early_stop(&w->blur_opacity);
|
2019-02-21 02:12:07 +00:00
|
|
|
}
|
|
|
|
|
2023-01-13 11:27:45 +00:00
|
|
|
// TODO(absolutelynothelix): rename to x_update_win_(randr_?)monitor and move to
|
|
|
|
// the x.c.
|
2023-06-29 04:39:36 +00:00
|
|
|
void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw) {
|
2023-01-13 11:27:45 +00:00
|
|
|
mw->randr_monitor = -1;
|
2023-06-29 04:39:36 +00:00
|
|
|
for (int i = 0; i < monitors->count; i++) {
|
|
|
|
auto e = pixman_region32_extents(&monitors->regions[i]);
|
2023-01-13 11:27:45 +00:00
|
|
|
if (e->x1 <= mw->g.x && e->y1 <= mw->g.y &&
|
|
|
|
e->x2 >= mw->g.x + mw->widthb && e->y2 >= mw->g.y + mw->heightb) {
|
|
|
|
mw->randr_monitor = i;
|
|
|
|
log_debug("Window %#010x (%s), %dx%d+%dx%d, is entirely on the "
|
|
|
|
"monitor %d (%dx%d+%dx%d)",
|
|
|
|
mw->base.id, mw->name, mw->g.x, mw->g.y, mw->widthb,
|
|
|
|
mw->heightb, i, e->x1, e->y1, e->x2 - e->x1, e->y2 - e->y1);
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2023-01-13 11:27:45 +00:00
|
|
|
log_debug("Window %#010x (%s), %dx%d+%dx%d, is not entirely on any monitor",
|
|
|
|
mw->base.id, mw->name, mw->g.x, mw->g.y, mw->widthb, mw->heightb);
|
2018-12-15 21:11:41 +00:00
|
|
|
}
|
2019-02-09 15:50:40 +00:00
|
|
|
|
|
|
|
/// Map an already registered window
|
2019-09-20 00:41:56 +00:00
|
|
|
void map_win_start(session_t *ps, struct managed_win *w) {
|
2024-03-24 02:02:14 +00:00
|
|
|
session_assert_server_grabbed(ps);
|
2019-03-10 12:34:37 +00:00
|
|
|
assert(w);
|
|
|
|
|
|
|
|
// Don't care about window mapping if it's an InputOnly window
|
|
|
|
// Also, try avoiding mapping a window twice
|
|
|
|
if (w->a._class == XCB_WINDOW_CLASS_INPUT_ONLY) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
log_debug("Mapping (%#010x \"%s\")", w->base.id, w->name);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2024-03-22 02:41:48 +00:00
|
|
|
assert(w->state != WSTATE_DESTROYED);
|
|
|
|
if (w->state == WSTATE_MAPPED) {
|
|
|
|
log_error("Mapping an already mapped window");
|
|
|
|
assert(false);
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-25 16:59:19 +00:00
|
|
|
// Rant: window size could change after we queried its geometry here and
|
|
|
|
// before we get its pixmap. Later, when we get back to the event
|
|
|
|
// processing loop, we will get the notification about size change from
|
|
|
|
// Xserver and try to refresh the pixmap, while the pixmap is actually
|
|
|
|
// already up-to-date (i.e. the notification is stale). There is basically
|
|
|
|
// no real way to prevent this, aside from grabbing the server.
|
2019-03-16 23:57:47 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// XXX Can we assume map_state is always viewable?
|
|
|
|
w->a.map_state = XCB_MAP_STATE_VIEWABLE;
|
|
|
|
|
|
|
|
// Update window mode here to check for ARGB windows
|
|
|
|
w->mode = win_calc_mode(w);
|
|
|
|
|
2024-02-20 00:41:55 +00:00
|
|
|
log_debug("Window (%#010x) has type %s", w->base.id, WINTYPES[w->window_type].name);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2024-03-22 02:41:48 +00:00
|
|
|
w->state = WSTATE_MAPPED;
|
|
|
|
win_start_fade(ps, w, 1);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-04-18 07:51:17 +00:00
|
|
|
log_debug("Window %#010x has opacity %f, opacity target is %f", w->base.id,
|
2024-03-19 16:44:02 +00:00
|
|
|
animatable_get(&w->opacity), w->opacity.target);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Send D-Bus signal
|
2024-03-24 02:02:14 +00:00
|
|
|
if (session_get_options(ps)->dbus) {
|
2024-03-23 14:01:46 +00:00
|
|
|
cdbus_ev_win_mapped(session_get_cdbus(ps), &w->base);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2019-02-21 02:12:07 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (!session_is_redirected(ps)) {
|
2024-03-22 02:41:48 +00:00
|
|
|
win_skip_fading(w);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2019-02-09 15:50:40 +00:00
|
|
|
}
|
|
|
|
|
2019-12-20 19:17:38 +00:00
|
|
|
/**
|
|
|
|
* Update target window opacity depending on the current state.
|
|
|
|
*/
|
|
|
|
void win_update_opacity_target(session_t *ps, struct managed_win *w) {
|
2024-03-22 02:41:48 +00:00
|
|
|
win_start_fade(ps, w, w->blur_opacity.target); // We don't want to change
|
|
|
|
// blur_opacity target
|
2019-12-20 19:17:38 +00:00
|
|
|
|
2024-03-21 18:01:02 +00:00
|
|
|
if (w->number_of_animations == 0) {
|
2019-12-20 19:17:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-03-19 16:44:02 +00:00
|
|
|
log_debug("Window %#010x (%s) opacity %f, opacity target %f, start %f", w->base.id,
|
|
|
|
w->name, animatable_get(&w->opacity), w->opacity.target, w->opacity.start);
|
2019-12-20 19:17:38 +00:00
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
if (!session_is_redirected(ps)) {
|
2024-03-22 02:41:48 +00:00
|
|
|
win_skip_fading(w);
|
2019-12-20 19:17:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
/**
|
|
|
|
* Find a managed window from window id in window linked list of the session.
|
|
|
|
*/
|
|
|
|
struct managed_win *find_managed_win(session_t *ps, xcb_window_t id) {
|
2024-03-24 02:02:14 +00:00
|
|
|
struct win *w = session_find_win(ps, id);
|
2019-04-17 22:14:45 +00:00
|
|
|
if (!w || !w->managed) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto mw = (struct managed_win *)w;
|
2024-03-22 02:41:48 +00:00
|
|
|
assert(mw->state != WSTATE_DESTROYED);
|
2019-04-17 22:14:45 +00:00
|
|
|
return mw;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-04-05 20:20:58 +00:00
|
|
|
* Find a managed window that is, or is a parent of `wid`.
|
2019-04-17 22:14:45 +00:00
|
|
|
*
|
|
|
|
* @param ps current session
|
|
|
|
* @param wid window ID
|
|
|
|
* @return struct _win object of the found window, NULL if not found
|
|
|
|
*/
|
2020-04-05 20:20:58 +00:00
|
|
|
struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid) {
|
2022-08-25 16:59:19 +00:00
|
|
|
// TODO(yshui) this should probably be an "update tree", then
|
2024-03-24 02:02:14 +00:00
|
|
|
// session_find_toplevel. current approach is a bit more "racy", as the server
|
2022-08-25 16:59:19 +00:00
|
|
|
// state might be ahead of our state
|
2019-04-17 22:14:45 +00:00
|
|
|
struct win *w = NULL;
|
|
|
|
|
|
|
|
// We traverse through its ancestors to find out the frame
|
2022-08-25 16:59:19 +00:00
|
|
|
// Using find_win here because if we found a unmanaged window we know
|
|
|
|
// about, we can stop early.
|
2024-03-24 02:02:14 +00:00
|
|
|
auto c = session_get_x_connection(ps);
|
|
|
|
while (wid && wid != c->screen_info->root && !(w = session_find_win(ps, wid))) {
|
2022-08-25 16:59:19 +00:00
|
|
|
// xcb_query_tree probably fails if you run picom when X is
|
|
|
|
// somehow initializing (like add it in .xinitrc). In this case
|
2019-04-17 22:14:45 +00:00
|
|
|
// just leave it alone.
|
2024-03-24 02:02:14 +00:00
|
|
|
auto reply = xcb_query_tree_reply(c->c, xcb_query_tree(c->c, wid), NULL);
|
2019-04-17 22:14:45 +00:00
|
|
|
if (reply == NULL) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
wid = reply->parent;
|
|
|
|
free(reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (w == NULL || !w->managed) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (struct managed_win *)w;
|
|
|
|
}
|
|
|
|
|
2020-04-06 19:52:32 +00:00
|
|
|
/// Set flags on a window. Some sanity checks are performed
|
|
|
|
void win_set_flags(struct managed_win *w, uint64_t flags) {
|
2020-10-28 19:02:57 +00:00
|
|
|
log_debug("Set flags %" PRIu64 " to window %#010x (%s)", flags, w->base.id, w->name);
|
2024-03-22 02:41:48 +00:00
|
|
|
if (unlikely(w->state == WSTATE_DESTROYED)) {
|
2020-04-06 19:52:32 +00:00
|
|
|
log_error("Flags set on a destroyed window %#010x (%s)", w->base.id, w->name);
|
2019-09-20 00:59:25 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-06 19:52:32 +00:00
|
|
|
w->flags |= flags;
|
2019-09-20 00:59:25 +00:00
|
|
|
}
|
|
|
|
|
2020-04-06 19:52:32 +00:00
|
|
|
/// Clear flags on a window. Some sanity checks are performed
|
|
|
|
void win_clear_flags(struct managed_win *w, uint64_t flags) {
|
2020-10-28 19:02:57 +00:00
|
|
|
log_debug("Clear flags %" PRIu64 " from window %#010x (%s)", flags, w->base.id,
|
|
|
|
w->name);
|
2024-03-22 02:41:48 +00:00
|
|
|
if (unlikely(w->state == WSTATE_DESTROYED)) {
|
2020-04-11 01:39:20 +00:00
|
|
|
log_warn("Flags cleared on a destroyed window %#010x (%s)", w->base.id,
|
|
|
|
w->name);
|
2020-04-06 19:52:32 +00:00
|
|
|
return;
|
2019-09-20 00:59:25 +00:00
|
|
|
}
|
|
|
|
|
2020-04-06 19:52:32 +00:00
|
|
|
w->flags = w->flags & (~flags);
|
|
|
|
}
|
|
|
|
|
2020-11-27 05:34:54 +00:00
|
|
|
void win_set_properties_stale(struct managed_win *w, const xcb_atom_t *props, int nprops) {
|
2023-12-17 08:55:23 +00:00
|
|
|
auto const bits_per_element = sizeof(*w->stale_props) * 8;
|
2020-11-27 05:34:54 +00:00
|
|
|
size_t new_capacity = w->stale_props_capacity;
|
|
|
|
|
|
|
|
// Calculate the new capacity of the properties array
|
|
|
|
for (int i = 0; i < nprops; i++) {
|
|
|
|
if (props[i] >= new_capacity * bits_per_element) {
|
|
|
|
new_capacity = props[i] / bits_per_element + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reallocate if necessary
|
|
|
|
if (new_capacity > w->stale_props_capacity) {
|
|
|
|
w->stale_props =
|
|
|
|
realloc(w->stale_props, new_capacity * sizeof(*w->stale_props));
|
2020-10-22 14:48:11 +00:00
|
|
|
|
|
|
|
// Clear the content of the newly allocated bytes
|
|
|
|
memset(w->stale_props + w->stale_props_capacity, 0,
|
2020-11-27 05:34:54 +00:00
|
|
|
(new_capacity - w->stale_props_capacity) * sizeof(*w->stale_props));
|
|
|
|
w->stale_props_capacity = new_capacity;
|
2020-10-22 14:48:11 +00:00
|
|
|
}
|
|
|
|
|
2020-11-27 05:34:54 +00:00
|
|
|
// Set the property bits
|
|
|
|
for (int i = 0; i < nprops; i++) {
|
|
|
|
w->stale_props[props[i] / bits_per_element] |=
|
|
|
|
1UL << (props[i] % bits_per_element);
|
|
|
|
}
|
2020-10-22 14:48:11 +00:00
|
|
|
win_set_flags(w, WIN_FLAGS_PROPERTY_STALE);
|
|
|
|
}
|
|
|
|
|
2020-10-23 00:51:28 +00:00
|
|
|
static void win_clear_all_properties_stale(struct managed_win *w) {
|
|
|
|
memset(w->stale_props, 0, w->stale_props_capacity * sizeof(*w->stale_props));
|
2020-10-22 14:48:11 +00:00
|
|
|
win_clear_flags(w, WIN_FLAGS_PROPERTY_STALE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool win_fetch_and_unset_property_stale(struct managed_win *w, xcb_atom_t prop) {
|
2023-12-17 08:55:23 +00:00
|
|
|
auto const bits_per_element = sizeof(*w->stale_props) * 8;
|
2020-10-22 14:48:11 +00:00
|
|
|
if (prop >= w->stale_props_capacity * bits_per_element) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-12-17 08:55:23 +00:00
|
|
|
auto const mask = 1UL << (prop % bits_per_element);
|
2020-10-22 14:48:11 +00:00
|
|
|
bool ret = w->stale_props[prop / bits_per_element] & mask;
|
|
|
|
w->stale_props[prop / bits_per_element] &= ~mask;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-04-06 19:52:32 +00:00
|
|
|
bool win_check_flags_any(struct managed_win *w, uint64_t flags) {
|
|
|
|
return (w->flags & flags) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool win_check_flags_all(struct managed_win *w, uint64_t flags) {
|
|
|
|
return (w->flags & flags) == flags;
|
2019-09-20 00:59:25 +00:00
|
|
|
}
|
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
/**
|
|
|
|
* Check if a window is a fullscreen window.
|
|
|
|
*
|
|
|
|
* It's not using w->border_size for performance measures.
|
|
|
|
*/
|
2024-03-24 02:02:14 +00:00
|
|
|
void win_update_is_fullscreen(session_t *ps, struct managed_win *w) {
|
|
|
|
auto options = session_get_options(ps);
|
|
|
|
if (!options->no_ewmh_fullscreen && w->is_ewmh_fullscreen) {
|
2024-02-14 18:54:04 +00:00
|
|
|
w->is_fullscreen = true;
|
|
|
|
return;
|
2020-10-23 01:08:34 +00:00
|
|
|
}
|
2024-03-24 02:02:14 +00:00
|
|
|
auto root_extent = session_get_root_extent(ps);
|
2024-02-14 18:54:04 +00:00
|
|
|
w->is_fullscreen = w->g.x <= 0 && w->g.y <= 0 &&
|
2024-03-24 02:02:14 +00:00
|
|
|
(w->g.x + w->widthb) >= root_extent.width &&
|
|
|
|
(w->g.y + w->heightb) >= root_extent.height &&
|
2024-02-14 18:54:04 +00:00
|
|
|
(!w->bounding_shaped || w->rounded_corners);
|
2019-04-17 22:14:45 +00:00
|
|
|
}
|
|
|
|
|
2020-01-18 18:00:35 +00:00
|
|
|
/**
|
|
|
|
* Check if a window has BYPASS_COMPOSITOR property set
|
|
|
|
*
|
2020-08-30 14:25:58 +00:00
|
|
|
* TODO(yshui) cache this property
|
2020-01-18 18:00:35 +00:00
|
|
|
*/
|
2024-03-24 02:02:14 +00:00
|
|
|
bool win_is_bypassing_compositor(session_t *ps, const struct managed_win *w) {
|
2020-01-18 18:00:35 +00:00
|
|
|
bool ret = false;
|
|
|
|
|
2024-03-24 02:02:14 +00:00
|
|
|
auto prop = x_get_prop(session_get_x_connection(ps), w->client_win,
|
|
|
|
session_get_atoms(ps)->a_NET_WM_BYPASS_COMPOSITOR, 1L,
|
|
|
|
XCB_ATOM_CARDINAL, 32);
|
2020-01-18 18:00:35 +00:00
|
|
|
|
|
|
|
if (prop.nitems && *prop.c32 == 1) {
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
free_winprop(&prop);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
/**
|
2022-08-25 16:59:19 +00:00
|
|
|
* Check if a window is focused, without using any focus rules or forced focus
|
|
|
|
* settings
|
2019-04-17 22:14:45 +00:00
|
|
|
*/
|
2024-02-15 00:07:31 +00:00
|
|
|
bool win_is_focused_raw(const struct managed_win *w) {
|
|
|
|
return w->a.map_state == XCB_MAP_STATE_VIEWABLE && w->is_ewmh_focused;
|
2019-04-17 22:14:45 +00:00
|
|
|
}
|