mirror of
https://github.com/yshui/picom.git
synced 2024-11-11 13:51:02 -05:00
Feature: Unredirect windows when there's a fullscreen window
- Optionally unredirect windows when there's a fullscreen opaque window on the top of the stack (--unredir-if-possible). Experimental. Known issues: * Screen flickers when redirecting/unredirecting windows. --paint-on-overlay seemingly minimizes it (Thanks for hints from mutter), but still noticeable. * It probably does not play well with vdpau in some cases. - A debug option DEBUG_REDIR is added. - Fix a bug that reg_ignore are not expired when a CirculateNotify is received. - Add extra safe guards in some places, which could be bad for performance. - Remove some abundant code.
This commit is contained in:
parent
e924976b28
commit
59e54b0665
2 changed files with 156 additions and 26 deletions
156
src/compton.c
156
src/compton.c
|
@ -74,6 +74,11 @@ XserverRegion screen_reg = None;
|
||||||
/// Current active window. Used by EWMH _NET_ACTIVE_WINDOW focus
|
/// Current active window. Used by EWMH _NET_ACTIVE_WINDOW focus
|
||||||
/// detection.
|
/// detection.
|
||||||
win *active_win = NULL;
|
win *active_win = NULL;
|
||||||
|
/// Whether all windows are currently redirected.
|
||||||
|
Bool redirected = False;
|
||||||
|
/// Whether there's a highest fullscreen window, and all windows could
|
||||||
|
/// be unredirected.
|
||||||
|
Bool unredir_possible = False;
|
||||||
|
|
||||||
/// Pregenerated alpha pictures.
|
/// Pregenerated alpha pictures.
|
||||||
Picture *alpha_picts = NULL;
|
Picture *alpha_picts = NULL;
|
||||||
|
@ -188,6 +193,7 @@ static options_t opts = {
|
||||||
.synchronize = False,
|
.synchronize = False,
|
||||||
.detect_rounded_corners = False,
|
.detect_rounded_corners = False,
|
||||||
.paint_on_overlay = False,
|
.paint_on_overlay = False,
|
||||||
|
.unredir_if_possible = False,
|
||||||
|
|
||||||
.refresh_rate = 0,
|
.refresh_rate = 0,
|
||||||
.sw_opti = False,
|
.sw_opti = False,
|
||||||
|
@ -1360,8 +1366,13 @@ get_alpha_pict_o(opacity_t o) {
|
||||||
|
|
||||||
static win *
|
static win *
|
||||||
paint_preprocess(Display *dpy, win *list) {
|
paint_preprocess(Display *dpy, win *list) {
|
||||||
|
// Initialize unredir_possible
|
||||||
|
unredir_possible = False;
|
||||||
|
|
||||||
win *w;
|
win *w;
|
||||||
win *t = NULL, *next = NULL;
|
win *t = NULL, *next = NULL;
|
||||||
|
// Trace whether it's the highest window to paint
|
||||||
|
Bool is_highest = True;
|
||||||
|
|
||||||
// Fading step calculation
|
// Fading step calculation
|
||||||
unsigned steps = (sub_unslong(get_time_ms(), fade_time)
|
unsigned steps = (sub_unslong(get_time_ms(), fade_time)
|
||||||
|
@ -1410,24 +1421,6 @@ paint_preprocess(Display *dpy, win *list) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to_paint) {
|
if (to_paint) {
|
||||||
// Fetch the picture and pixmap if needed
|
|
||||||
if (!w->picture) {
|
|
||||||
XRenderPictureAttributes pa;
|
|
||||||
XRenderPictFormat *format;
|
|
||||||
Drawable draw = w->id;
|
|
||||||
|
|
||||||
if (has_name_pixmap && !w->pixmap) {
|
|
||||||
set_ignore(dpy, NextRequest(dpy));
|
|
||||||
w->pixmap = XCompositeNameWindowPixmap(dpy, w->id);
|
|
||||||
}
|
|
||||||
if (w->pixmap) draw = w->pixmap;
|
|
||||||
|
|
||||||
format = XRenderFindVisualFormat(dpy, w->a.visual);
|
|
||||||
pa.subwindow_mode = IncludeInferiors;
|
|
||||||
w->picture = XRenderCreatePicture(
|
|
||||||
dpy, draw, format, CPSubwindowMode, &pa);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch bounding region
|
// Fetch bounding region
|
||||||
if (!w->border_size) {
|
if (!w->border_size) {
|
||||||
w->border_size = border_size(dpy, w);
|
w->border_size = border_size(dpy, w);
|
||||||
|
@ -1482,6 +1475,10 @@ paint_preprocess(Display *dpy, win *list) {
|
||||||
!= (w->to_paint && WINDOW_SOLID == mode_old))
|
!= (w->to_paint && WINDOW_SOLID == mode_old))
|
||||||
reg_ignore_expire = True;
|
reg_ignore_expire = True;
|
||||||
|
|
||||||
|
// Add window to damaged area if its painting status changes
|
||||||
|
if (to_paint != w->to_paint)
|
||||||
|
add_damage_win(dpy, w);
|
||||||
|
|
||||||
if (to_paint) {
|
if (to_paint) {
|
||||||
// Generate ignore region for painting to reduce GPU load
|
// Generate ignore region for painting to reduce GPU load
|
||||||
if (reg_ignore_expire || !w->to_paint) {
|
if (reg_ignore_expire || !w->to_paint) {
|
||||||
|
@ -1516,6 +1513,14 @@ paint_preprocess(Display *dpy, win *list) {
|
||||||
|
|
||||||
last_reg_ignore = w->reg_ignore;
|
last_reg_ignore = w->reg_ignore;
|
||||||
|
|
||||||
|
if (is_highest && to_paint) {
|
||||||
|
is_highest = False;
|
||||||
|
if (WINDOW_SOLID == w->mode
|
||||||
|
&& (!w->frame_opacity || !win_has_frame(w))
|
||||||
|
&& win_is_fullscreen(w))
|
||||||
|
unredir_possible = True;
|
||||||
|
}
|
||||||
|
|
||||||
// Reset flags
|
// Reset flags
|
||||||
w->flags = 0;
|
w->flags = 0;
|
||||||
}
|
}
|
||||||
|
@ -1536,6 +1541,37 @@ paint_preprocess(Display *dpy, win *list) {
|
||||||
w->to_paint = to_paint;
|
w->to_paint = to_paint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If possible, unredirect all windows and stop painting
|
||||||
|
if (opts.unredir_if_possible && unredir_possible) {
|
||||||
|
redir_stop(dpy);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
redir_start(dpy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch pictures only if windows are redirected
|
||||||
|
if (redirected) {
|
||||||
|
for (w = t; w; w = w->prev_trans) {
|
||||||
|
// Fetch the picture and pixmap if needed
|
||||||
|
if (!w->picture) {
|
||||||
|
XRenderPictureAttributes pa;
|
||||||
|
XRenderPictFormat *format;
|
||||||
|
Drawable draw = w->id;
|
||||||
|
|
||||||
|
if (has_name_pixmap && !w->pixmap) {
|
||||||
|
set_ignore(dpy, NextRequest(dpy));
|
||||||
|
w->pixmap = XCompositeNameWindowPixmap(dpy, w->id);
|
||||||
|
}
|
||||||
|
if (w->pixmap) draw = w->pixmap;
|
||||||
|
|
||||||
|
format = XRenderFindVisualFormat(dpy, w->a.visual);
|
||||||
|
pa.subwindow_mode = IncludeInferiors;
|
||||||
|
w->picture = XRenderCreatePicture(
|
||||||
|
dpy, draw, format, CPSubwindowMode, &pa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2053,7 +2089,7 @@ static void
|
||||||
unmap_win(Display *dpy, Window id, Bool fade) {
|
unmap_win(Display *dpy, Window id, Bool fade) {
|
||||||
win *w = find_win(id);
|
win *w = find_win(id);
|
||||||
|
|
||||||
if (!w) return;
|
if (!w || IsUnmapped == w->a.map_state) return;
|
||||||
|
|
||||||
w->a.map_state = IsUnmapped;
|
w->a.map_state = IsUnmapped;
|
||||||
|
|
||||||
|
@ -2464,6 +2500,8 @@ static void
|
||||||
restack_win(Display *dpy, win *w, Window new_above) {
|
restack_win(Display *dpy, win *w, Window new_above) {
|
||||||
Window old_above;
|
Window old_above;
|
||||||
|
|
||||||
|
update_reg_ignore_expire(w);
|
||||||
|
|
||||||
if (w->next) {
|
if (w->next) {
|
||||||
old_above = w->next->id;
|
old_above = w->next->id;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2593,7 +2631,7 @@ configure_win(Display *dpy, XConfigureEvent *ce) {
|
||||||
win_update_shape(dpy, w);
|
win_update_shape(dpy, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (w->a.map_state != IsUnmapped && damage) {
|
if (damage) {
|
||||||
XserverRegion extents = win_extents(dpy, w);
|
XserverRegion extents = win_extents(dpy, w);
|
||||||
XFixesUnionRegion(dpy, damage, damage, extents);
|
XFixesUnionRegion(dpy, damage, damage, extents);
|
||||||
XFixesDestroyRegion(dpy, extents);
|
XFixesDestroyRegion(dpy, extents);
|
||||||
|
@ -3499,6 +3537,10 @@ usage(void) {
|
||||||
"--respect-attr-shadow\n"
|
"--respect-attr-shadow\n"
|
||||||
" Respect _COMPTON_SHADOW. This a prototype-level feature, which\n"
|
" Respect _COMPTON_SHADOW. This a prototype-level feature, which\n"
|
||||||
" you must not rely on.\n"
|
" you must not rely on.\n"
|
||||||
|
"--unredir-if-possible\n"
|
||||||
|
" Unredirect all windows if a full-screen opaque window is\n"
|
||||||
|
" detected, to maximize performance for full-screen windows.\n"
|
||||||
|
" Experimental.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Format of a condition:\n"
|
"Format of a condition:\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -3930,6 +3972,7 @@ get_cfg(int argc, char *const *argv) {
|
||||||
{ "vsync-aggressive", no_argument, NULL, 275 },
|
{ "vsync-aggressive", no_argument, NULL, 275 },
|
||||||
{ "use-ewmh-active-win", no_argument, NULL, 276 },
|
{ "use-ewmh-active-win", no_argument, NULL, 276 },
|
||||||
{ "respect-attr-shadow", no_argument, NULL, 277 },
|
{ "respect-attr-shadow", no_argument, NULL, 277 },
|
||||||
|
{ "unredir-if-possible", no_argument, NULL, 278 },
|
||||||
// Must terminate with a NULL entry
|
// Must terminate with a NULL entry
|
||||||
{ NULL, 0, NULL, 0 },
|
{ NULL, 0, NULL, 0 },
|
||||||
};
|
};
|
||||||
|
@ -4124,6 +4167,10 @@ get_cfg(int argc, char *const *argv) {
|
||||||
// --respect-attr-shadow
|
// --respect-attr-shadow
|
||||||
opts.respect_attr_shadow = True;
|
opts.respect_attr_shadow = True;
|
||||||
break;
|
break;
|
||||||
|
case 278:
|
||||||
|
// --unredir-if-possible
|
||||||
|
opts.unredir_if_possible = True;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
break;
|
break;
|
||||||
|
@ -4522,6 +4569,61 @@ init_overlay(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect all windows.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
redir_start(Display *dpy) {
|
||||||
|
if (!redirected) {
|
||||||
|
#ifdef DEBUG_REDIR
|
||||||
|
printf("redir_start(): Screen redirected.\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Map overlay window. Done firstly according to this:
|
||||||
|
// https://bugzilla.gnome.org/show_bug.cgi?id=597014
|
||||||
|
if (overlay)
|
||||||
|
XMapWindow(dpy, overlay);
|
||||||
|
|
||||||
|
XCompositeRedirectSubwindows(dpy, root, CompositeRedirectManual);
|
||||||
|
|
||||||
|
// Must call XSync() here
|
||||||
|
XSync(dpy, False);
|
||||||
|
|
||||||
|
redirected = True;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unredirect all windows.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
redir_stop(Display *dpy) {
|
||||||
|
if (redirected) {
|
||||||
|
#ifdef DEBUG_REDIR
|
||||||
|
printf("redir_stop(): Screen unredirected.\n");
|
||||||
|
#endif
|
||||||
|
// Destroy all Pictures as they expire once windows are unredirected
|
||||||
|
// If we don't destroy them here, looks like the resources are just
|
||||||
|
// kept inaccessible somehow
|
||||||
|
for (win *w = list; w; w = w->next) {
|
||||||
|
free_pixmap(dpy, &w->pixmap);
|
||||||
|
free_picture(dpy, &w->picture);
|
||||||
|
}
|
||||||
|
|
||||||
|
XCompositeUnredirectSubwindows(dpy, root, CompositeRedirectManual);
|
||||||
|
|
||||||
|
// Unmap overlay window
|
||||||
|
if (overlay)
|
||||||
|
XUnmapWindow(dpy, overlay);
|
||||||
|
|
||||||
|
// Must call XSync() here
|
||||||
|
XSync(dpy, False);
|
||||||
|
|
||||||
|
redirected = False;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv) {
|
main(int argc, char **argv) {
|
||||||
XEvent ev;
|
XEvent ev;
|
||||||
|
@ -4689,8 +4791,7 @@ main(int argc, char **argv) {
|
||||||
all_damage = None;
|
all_damage = None;
|
||||||
XGrabServer(dpy);
|
XGrabServer(dpy);
|
||||||
|
|
||||||
XCompositeRedirectSubwindows(
|
redir_start(dpy);
|
||||||
dpy, root, CompositeRedirectManual);
|
|
||||||
|
|
||||||
XSelectInput(dpy, root,
|
XSelectInput(dpy, root,
|
||||||
SubstructureNotifyMask
|
SubstructureNotifyMask
|
||||||
|
@ -4725,6 +4826,7 @@ main(int argc, char **argv) {
|
||||||
|
|
||||||
t = paint_preprocess(dpy, list);
|
t = paint_preprocess(dpy, list);
|
||||||
|
|
||||||
|
if (redirected)
|
||||||
paint_all(dpy, None, t);
|
paint_all(dpy, None, t);
|
||||||
|
|
||||||
// Initialize idling
|
// Initialize idling
|
||||||
|
@ -4737,9 +4839,9 @@ main(int argc, char **argv) {
|
||||||
while (XEventsQueued(dpy, QueuedAfterReading)
|
while (XEventsQueued(dpy, QueuedAfterReading)
|
||||||
|| (evpoll(&ufd,
|
|| (evpoll(&ufd,
|
||||||
(ev_received ? 0: (idling ? -1: fade_timeout()))) > 0)) {
|
(ev_received ? 0: (idling ? -1: fade_timeout()))) > 0)) {
|
||||||
// Sometimes poll() returns 1 but no events are actually read, causing
|
// Sometimes poll() returns 1 but no events are actually read,
|
||||||
// XNextEvent() to block, I have no idea what's wrong, so we check for the
|
// causing XNextEvent() to block, I have no idea what's wrong, so we
|
||||||
// number of events here
|
// check for the number of events here
|
||||||
if (XEventsQueued(dpy, QueuedAfterReading)) {
|
if (XEventsQueued(dpy, QueuedAfterReading)) {
|
||||||
XNextEvent(dpy, &ev);
|
XNextEvent(dpy, &ev);
|
||||||
ev_handle((XEvent *) &ev);
|
ev_handle((XEvent *) &ev);
|
||||||
|
@ -4752,6 +4854,10 @@ main(int argc, char **argv) {
|
||||||
|
|
||||||
t = paint_preprocess(dpy, list);
|
t = paint_preprocess(dpy, list);
|
||||||
|
|
||||||
|
// If the screen is unredirected, free all_damage to stop painting
|
||||||
|
if (!redirected)
|
||||||
|
free_region(dpy, &all_damage);
|
||||||
|
|
||||||
if (all_damage && !is_region_empty(dpy, all_damage)) {
|
if (all_damage && !is_region_empty(dpy, all_damage)) {
|
||||||
static int paint;
|
static int paint;
|
||||||
paint_all(dpy, all_damage, t);
|
paint_all(dpy, all_damage, t);
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
// #define DEBUG_CLIENTWIN 1
|
// #define DEBUG_CLIENTWIN 1
|
||||||
// #define DEBUG_WINDATA 1
|
// #define DEBUG_WINDATA 1
|
||||||
// #define DEBUG_WINMATCH 1
|
// #define DEBUG_WINMATCH 1
|
||||||
|
// #define DEBUG_REDIR 1
|
||||||
// #define MONITOR_REPAINT 1
|
// #define MONITOR_REPAINT 1
|
||||||
|
|
||||||
// Whether to enable PCRE regular expression support in blacklists, enabled
|
// Whether to enable PCRE regular expression support in blacklists, enabled
|
||||||
|
@ -367,6 +368,9 @@ typedef struct _options {
|
||||||
/// Whether to paint on X Composite overlay window instead of root
|
/// Whether to paint on X Composite overlay window instead of root
|
||||||
/// window.
|
/// window.
|
||||||
Bool paint_on_overlay;
|
Bool paint_on_overlay;
|
||||||
|
/// Whether to unredirect all windows if a full-screen opaque window
|
||||||
|
/// is detected.
|
||||||
|
Bool unredir_if_possible;
|
||||||
/// Whether to work under synchronized mode for debugging.
|
/// Whether to work under synchronized mode for debugging.
|
||||||
Bool synchronize;
|
Bool synchronize;
|
||||||
|
|
||||||
|
@ -960,12 +964,26 @@ update_reg_ignore_expire(const win *w) {
|
||||||
reg_ignore_expire = True;
|
reg_ignore_expire = True;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a window has WM frames.
|
||||||
|
*/
|
||||||
static inline bool
|
static inline bool
|
||||||
win_has_frame(const win *w) {
|
win_has_frame(const win *w) {
|
||||||
return w->top_width || w->left_width || w->right_width
|
return w->top_width || w->left_width || w->right_width
|
||||||
|| w->bottom_width;
|
|| w->bottom_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a window is a fullscreen window.
|
||||||
|
*
|
||||||
|
* It's not using w->border_size for performance measures.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
win_is_fullscreen(const win *w) {
|
||||||
|
return (w->a.x <= 0 && w->a.y <= 0 && (w->a.x + w->widthb) >= root_width
|
||||||
|
&& (w->a.y + w->heightb) >= root_height && !w->bounding_shaped);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
win_rounded_corners(Display *dpy, win *w);
|
win_rounded_corners(Display *dpy, win *w);
|
||||||
|
|
||||||
|
@ -1345,3 +1363,9 @@ init_dbe(void);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
init_overlay(void);
|
init_overlay(void);
|
||||||
|
|
||||||
|
static void
|
||||||
|
redir_start(Display *dpy);
|
||||||
|
|
||||||
|
static void
|
||||||
|
redir_stop(Display *dpy);
|
||||||
|
|
Loading…
Reference in a new issue