Skip to content

Commit

Permalink
Merge pull request #1220 from yshui/generic-fading
Browse files Browse the repository at this point in the history
Animation part I
  • Loading branch information
yshui committed Mar 26, 2024
2 parents 8f53fa6 + 3b6e003 commit 21777fc
Show file tree
Hide file tree
Showing 11 changed files with 611 additions and 438 deletions.
45 changes: 11 additions & 34 deletions src/backend/backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "config.h"
#include "log.h"
#include "region.h"
#include "transition.h"
#include "types.h"
#include "win.h"
#include "x.h"
Expand Down Expand Up @@ -253,45 +254,20 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
auto real_win_mode = w->mode;
coord_t window_coord = {.x = w->g.x, .y = w->g.y};

const double window_opacity = animatable_get(&w->opacity);
if (w->blur_background &&
(ps->o.force_win_blend || real_win_mode == WMODE_TRANS ||
(ps->o.blur_background_frame && real_win_mode == WMODE_FRAME_TRANS))) {
// Minimize the region we try to blur, if the window
// itself is not opaque, only the frame is.

double blur_opacity = 1;
if (w->opacity < (1.0 / MAX_ALPHA)) {
// Hide blur for fully transparent windows.
blur_opacity = 0;
} else if (w->state == WSTATE_MAPPING) {
// Gradually increase the blur intensity during
// fading in.
assert(w->opacity <= w->opacity_target);
blur_opacity = w->opacity / w->opacity_target;
} else if (w->state == WSTATE_UNMAPPING ||
w->state == WSTATE_DESTROYING) {
// Gradually decrease the blur intensity during
// fading out.
assert(w->opacity <= w->opacity_target_old);
blur_opacity = w->opacity / w->opacity_target_old;
} else if (w->state == WSTATE_FADING) {
if (w->opacity < w->opacity_target &&
w->opacity_target_old < (1.0 / MAX_ALPHA)) {
// Gradually increase the blur intensity during
// fading in.
assert(w->opacity <= w->opacity_target);
blur_opacity = w->opacity / w->opacity_target;
} else if (w->opacity > w->opacity_target &&
w->opacity_target < (1.0 / MAX_ALPHA)) {
// Gradually decrease the blur intensity during
// fading out.
assert(w->opacity <= w->opacity_target_old);
blur_opacity = w->opacity / w->opacity_target_old;
}
}
const double blur_opacity = animatable_get(&w->blur_opacity);
assert(blur_opacity >= 0 && blur_opacity <= 1);

if (real_win_mode == WMODE_TRANS || ps->o.force_win_blend) {
if (blur_opacity * MAX_ALPHA < 1) {
// We don't need to blur if it would be completely
// transparent
} else if (real_win_mode == WMODE_TRANS || ps->o.force_win_blend) {
// We need to blur the bounding shape of the window
// (reg_paint_in_bound = reg_bound \cap reg_paint)
ps->backend_data->ops->blur(
Expand Down Expand Up @@ -404,7 +380,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
if (w->dim) {
dim_opacity = ps->o.inactive_dim;
if (!ps->o.inactive_dim_fixed) {
dim_opacity *= w->opacity;
dim_opacity *= window_opacity;
}
}

Expand All @@ -418,7 +394,8 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
ps->backend_data, IMAGE_PROPERTY_DIM_LEVEL, w->win_image,
&dim_opacity);
ps->backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_OPACITY, w->win_image, &w->opacity);
ps->backend_data, IMAGE_PROPERTY_OPACITY, w->win_image,
&window_opacity);
ps->backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_CORNER_RADIUS, w->win_image,
(double[]){w->corner_radius});
Expand All @@ -440,7 +417,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
w->fg_shader ? (void *)w->fg_shader->backend_shader : NULL);
}

if (w->opacity * MAX_ALPHA < 1) {
if (window_opacity * MAX_ALPHA < 1) {
// We don't need to paint the window body itself if it's
// completely transparent.
goto skip;
Expand Down
11 changes: 8 additions & 3 deletions src/dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
#include "list.h"
#include "log.h"
#include "string_utils.h"
#include "transition.h"
#include "types.h"
#include "uthash_extra.h"
#include "utils.h"
#include "win.h"
#include "win_defs.h"

#include "dbus.h"

Expand Down Expand Up @@ -876,7 +878,7 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_

if (!strcmp("Mapped", target)) {
cdbus_reply(ps, msg, cdbus_append_bool_variant,
(bool[]){win_is_mapped_in_x(w)});
(bool[]){w->state == WSTATE_MAPPED});
return true;
}

Expand Down Expand Up @@ -991,14 +993,17 @@ static bool cdbus_process_win_get(session_t *ps, DBusMessage *msg) {
cdbus_m_win_get_do(class_general, cdbus_reply_string);
cdbus_m_win_get_do(role, cdbus_reply_string);

cdbus_m_win_get_do(opacity, cdbus_reply_double);
cdbus_m_win_get_do(opacity_target, cdbus_reply_double);
cdbus_m_win_get_do(opacity.target, cdbus_reply_double);
cdbus_m_win_get_do(has_opacity_prop, cdbus_reply_bool);
cdbus_m_win_get_do(opacity_prop, cdbus_reply_uint32);
cdbus_m_win_get_do(opacity_is_set, cdbus_reply_bool);
cdbus_m_win_get_do(opacity_set, cdbus_reply_double);

cdbus_m_win_get_do(frame_opacity, cdbus_reply_double);
if (strcmp(target, "opacity") == 0) {
cdbus_reply_double(ps, msg, animatable_get(&w->opacity));
return true;
}
if (!strcmp("left_width", target)) {
cdbus_reply_int32(ps, msg, w->frame_extents.left);
return true;
Expand Down
62 changes: 40 additions & 22 deletions src/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "region.h"
#include "utils.h"
#include "win.h"
#include "win_defs.h"
#include "x.h"

/// Event handling with X is complicated. Handling events with other events possibly
Expand Down Expand Up @@ -273,18 +274,27 @@ static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *
assert(&mw->base == w);
mw = NULL;
}

// A window can't be a client window and a top-level window at the same time,
// so only one of `w` and `mw` can be non-NULL
assert(w == NULL || mw == NULL);

if (w != NULL) {
auto _ attr_unused = destroy_win_start(ps, w);
} else if (mw != NULL) {
destroy_win_start(ps, w);
if (!w->managed || !((struct managed_win *)w)->to_paint) {
// If the window wasn't managed, or was already not rendered,
// we don't need to fade it out.
destroy_win_finish(ps, w);
}
return;
}
if (mw != NULL) {
win_unmark_client(ps, mw);
win_set_flags(mw, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true;
} else {
log_debug("Received a destroy notify from an unknown window, %#010x",
ev->window);
return;
}
log_debug("Received a destroy notify from an unknown window, %#010x", ev->window);
}

static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
Expand All @@ -308,6 +318,13 @@ static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
}

win_set_flags(w, WIN_FLAGS_MAPPED);
// We set `ever_damaged` to false here, instead of in `map_win_start`,
// because we might receive damage events before that function is called
// (which is called when we handle the `WIN_FLAGS_MAPPED` flag), in
// which case `repair_win` will be called, which uses `ever_damaged` so
// it needs to be correct. This also covers the case where the window is
// unmapped before `map_win_start` is called.
w->ever_damaged = false;

// FocusIn/Out may be ignored when the window is unmapped, so we must
// recheck focus here
Expand Down Expand Up @@ -349,8 +366,7 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
{
auto w = find_win(ps, ev->window);
if (w) {
auto ret = destroy_win_start(ps, w);
if (!ret && w->managed) {
if (w->managed) {
auto mw = (struct managed_win *)w;
// Usually, damage for unmapped windows
// are added in `paint_preprocess`, when
Expand All @@ -362,8 +378,17 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
if (mw->to_paint) {
add_damage_from_win(ps, mw);
}
CHECK(win_skip_fading(ps, mw));
// Emulating what X server does: a destroyed
// window is always unmapped first.
if (mw->state == WSTATE_MAPPED) {
unmap_win_start(ps, mw);
}
}
// Window reparenting is unlike normal window destruction,
// This window is going to be rendered under another
// parent, so we don't fade here.
destroy_win_start(ps, w);
destroy_win_finish(ps, w);
}
}

Expand All @@ -378,25 +403,14 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE;
} else {
auto w_real_top = find_managed_window_or_parent(ps, ev->parent);
if (w_real_top && w_real_top->state != WSTATE_UNMAPPED &&
w_real_top->state != WSTATE_UNMAPPING) {
if (w_real_top) {
log_debug("Mark window %#010x (%s) as having a stale "
"client",
w_real_top->base.id, w_real_top->name);
win_set_flags(w_real_top, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true;
} else {
if (!w_real_top) {
log_debug("parent %#010x not found", ev->parent);
} else {
// Window is not currently mapped, unmark its
// client to trigger a client recheck when it is
// mapped later.
win_unmark_client(ps, w_real_top);
log_debug("parent %#010x (%s) is in state %d",
w_real_top->base.id, w_real_top->name,
w_real_top->state);
}
log_debug("parent %#010x not found", ev->parent);
}
}
XCB_AWAIT_VOID(xcb_change_window_attributes, ps->c.c, ev->window,
Expand Down Expand Up @@ -605,7 +619,7 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t

static inline void repair_win(session_t *ps, struct managed_win *w) {
// Only mapped window can receive damages
assert(win_is_mapped_in_x(w));
assert(w->state == WSTATE_MAPPED || win_check_flags_all(w, WIN_FLAGS_MAPPED));

region_t parts;
pixman_region32_init(&parts);
Expand All @@ -628,6 +642,10 @@ static inline void repair_win(session_t *ps, struct managed_win *w) {
free(e);
}
win_extents(w, &parts);

// We only binds the window pixmap once the window is damaged.
win_set_flags(w, WIN_FLAGS_PIXMAP_STALE);
ps->pending_updates = true;
} else {
auto cookie = xcb_damage_subtract(ps->c.c, w->damage, XCB_NONE,
ps->damage_ring.x_region);
Expand Down
2 changes: 1 addition & 1 deletion src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ base_deps = [
srcs = [ files('picom.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', 'utils.c',
'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c', 'log.c',
'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c', 'statistics.c',
'vblank.c') ]
'vblank.c', 'transition.c') ]
picom_inc = include_directories('.')

cflags = []
Expand Down
Loading

0 comments on commit 21777fc

Please sign in to comment.