Skip to content

Commit

Permalink
Fix fast switch window hang
Browse files Browse the repository at this point in the history
A longstanding bug meant that after a fast switch, the some windows
might hang, and no longer get damaged.

To fix this I added a new debug view which prints all the components
currently attached to a window (given that at least physical is there as
well). It's super handy.

Turns out that i wasn't subtracting damage when the window was unmapped,
which meant that we didn't receive any new damage.

There's still a slight issue, where mapping and unmapping in the same
frame causes the system to get out of sync with the XServer. It should
be fixable with a bit more work, but for now you can at least fix it by
just unmapping and remapping again.
  • Loading branch information
Jesper Jensen committed Nov 29, 2018
1 parent aa38a5d commit 0281033
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 69 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ SOURCES += shaders/shaderinfo.c shaders/include.c
SOURCES += blur.c shadow.c texture.c renderutil.c textureeffects.c
SOURCES += framebuffer.c renderbuffer.c window.c windowlist.c xorg.c xtexture.c
SOURCES += profiler/zone.c profiler/render.c profiler/dump_events.c profiler/malloc_profile.c
SOURCES += debug.c

TEST_SOURCES = $(wildcard test/*.c)

Expand Down
149 changes: 80 additions & 69 deletions src/compton.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "timer.h"
#include "timeout.h"
#include "paths.h"
#include "debug.h"

#include "assets/assets.h"
#include "assets/shader.h"
Expand Down Expand Up @@ -808,6 +809,10 @@ static void map_win(session_t *ps, win_id wid) {
attribs.height + w->border_size * 2,
}};

if(swiss_hasComponent(&ps->win_list, COMPONENT_UNMAP, wid)) {
swiss_removeComponent(&ps->win_list, COMPONENT_UNMAP, wid);
}

struct StatefulComponent* stateful = swiss_getComponent(&ps->win_list, COMPONENT_STATEFUL, wid);
stateful->state = STATE_WAITING;
swiss_ensureComponent(&ps->win_list, COMPONENT_FOCUS_CHANGE, wid);
Expand All @@ -834,6 +839,9 @@ static void unmap_win(session_t *ps, win *w) {
ps->active_win = NULL;

swiss_ensureComponent(&ps->win_list, COMPONENT_UNMAP, wid);
if(swiss_hasComponent(&ps->win_list, COMPONENT_MAP, wid)) {
swiss_removeComponent(&ps->win_list, COMPONENT_MAP, wid);
}

// don't care about properties anymore
win_ev_stop(ps, w);
Expand Down Expand Up @@ -1264,19 +1272,21 @@ static void damage_win(session_t *ps, XDamageNotifyEvent *de) {
// know how to detect if a damage is caused by a move at this time.
// - Delusional 16/11-2018


win *w = find_win(ps, de->drawable);

if (!w)
if (!w) {
return;
win_id wid = swiss_indexOfPointer(&ps->win_list, COMPONENT_MUD, w);
}

// We need to subtract the damage, even if we aren't mapped. If we don't
// subtract the damage, we won't be notified of any new damage in the
// future.
XDamageSubtract(ps->dpy, w->damage, None, None);

win_id wid = swiss_indexOfPointer(&ps->win_list, COMPONENT_MUD, w);
if (!win_mapped(&ps->win_list, wid))
return;

//Reset the XDamage region, so we continue to recieve new damage
XDamageSubtract(ps->dpy, w->damage, None, None);

swiss_ensureComponent(&ps->win_list, COMPONENT_CONTENTS_DAMAGED, wid);
// @CLEANUP: We shouldn't damage the shadow here. It's more of an update
// thing. Maybe make a function for quick or?
Expand Down Expand Up @@ -1693,44 +1703,6 @@ ev_name(session_t *ps, XEvent *ev) {
return buf;
}

static Window
ev_window(session_t *ps, XEvent *ev) {
switch (ev->type) {
case CreateNotify:
return ev->xcreatewindow.window;
case ConfigureNotify:
return ev->xconfigure.window;
case DestroyNotify:
return ev->xdestroywindow.window;
case MapNotify:
return ev->xmap.window;
case UnmapNotify:
return ev->xunmap.window;
case ReparentNotify:
return ev->xreparent.window;
case CirculateNotify:
return ev->xcirculate.window;
case Expose:
return ev->xexpose.window;
case PropertyNotify:
return ev->xproperty.window;
case ClientMessage:
return ev->xclient.window;
default:
if (xorgContext_version(&ps->capabilities, PROTO_DAMAGE) >= XVERSION_YES
&& xorgContext_convertEvent(&ps->capabilities, PROTO_DAMAGE, ev->type) == XDamageNotify) {
return ((XDamageNotifyEvent *)ev)->drawable;
}

if (xorgContext_version(&ps->capabilities, PROTO_SHAPE) >= XVERSION_YES
&& xorgContext_convertEvent(&ps->capabilities, PROTO_SHAPE, ev->type) == ShapeNotify) {
return ((XShapeEvent *) ev)->window;
}

return 0;
}
}

static inline const char *
ev_focus_mode_name(XFocusChangeEvent* ev) {
switch (ev->mode) {
Expand Down Expand Up @@ -2035,6 +2007,43 @@ static void ev_screen_change_notify(session_t *ps, XRRScreenChangeNotifyEvent __
cxinerama_upd_scrs(ps);
}

static Window ev_window(session_t *ps, XEvent *ev) {
switch (ev->type) {
case CreateNotify:
return ev->xcreatewindow.window;
case ConfigureNotify:
return ev->xconfigure.window;
case DestroyNotify:
return ev->xdestroywindow.window;
case MapNotify:
return ev->xmap.window;
case UnmapNotify:
return ev->xunmap.window;
case ReparentNotify:
return ev->xreparent.window;
case CirculateNotify:
return ev->xcirculate.window;
case Expose:
return ev->xexpose.window;
case PropertyNotify:
return ev->xproperty.window;
case ClientMessage:
return ev->xclient.window;
default:
if (xorgContext_version(&ps->capabilities, PROTO_DAMAGE) >= XVERSION_YES
&& xorgContext_convertEvent(&ps->capabilities, PROTO_DAMAGE, ev->type) == XDamageNotify) {
return ((XDamageNotifyEvent *)ev)->drawable;
}

if (xorgContext_version(&ps->capabilities, PROTO_SHAPE) >= XVERSION_YES
&& xorgContext_convertEvent(&ps->capabilities, PROTO_SHAPE, ev->type) == ShapeNotify) {
return ((XShapeEvent *) ev)->window;
}

return 0;
}
}

static void
ev_handle(session_t *ps, XEvent *ev) {
if ((ev->type & 0x7f) != KeymapNotify) {
Expand Down Expand Up @@ -5055,8 +5064,6 @@ void session_run(session_t *ps) {

w->window_type = wintypeChanged->newType;
}

swiss_resetComponent(em, COMPONENT_WINTYPE_CHANGE);
zone_leave(&ZONE_update_wintype);


Expand Down Expand Up @@ -5164,28 +5171,6 @@ void session_run(session_t *ps) {
}
zone_leave(&ZONE_make_cutout);

zone_enter(&ZONE_remove_input);
// Remove all the X events
// @CLEANUP: Should maybe be done last in the frame
swiss_resetComponent(&ps->win_list, COMPONENT_MAP);
swiss_resetComponent(&ps->win_list, COMPONENT_UNMAP);
swiss_resetComponent(&ps->win_list, COMPONENT_MOVE);
swiss_removeComponentWhere(
&ps->win_list,
COMPONENT_RESIZE,
(enum ComponentType[]){COMPONENT_PHYSICAL, CQ_END}
);

for_components(it, em, COMPONENT_SHAPE_DAMAGED, CQ_END) {
struct ShapeDamagedEvent* shapeDamaged = swiss_getComponent(em, COMPONENT_SHAPE_DAMAGED, it.id);
if(shapeDamaged->rects.elementSize != 0) {
vector_kill(&shapeDamaged->rects);
}
}
swiss_resetComponent(&ps->win_list, COMPONENT_SHAPE_DAMAGED);

zone_leave(&ZONE_remove_input);

zone_enter(&ZONE_prop_blur_damage);
// Damage the blur of windows on top of damaged windows
for_components(it, &ps->win_list,
Expand Down Expand Up @@ -5213,7 +5198,6 @@ void session_run(session_t *ps) {
update_focused_state(&ps->win_list, ps);
calculate_window_opacity(ps, &ps->win_list);
start_focus_fade(&ps->win_list, ps->o.opacity_fade_time, ps->o.dim_fade_time);
swiss_resetComponent(&ps->win_list, COMPONENT_FOCUS_CHANGE);

zone_enter(&ZONE_update_fade);

Expand Down Expand Up @@ -5285,6 +5269,7 @@ void session_run(session_t *ps) {

#ifdef DEBUG_WINDOWS
/* windowlist_drawDebug(&ps->win_list, ps); */
draw_component_debug(&ps->win_list, &ps->root_size);
#endif

vector_kill(&opaque_shadow);
Expand Down Expand Up @@ -5318,6 +5303,32 @@ void session_run(session_t *ps) {
XSync(ps->dpy, False);
}

zone_enter(&ZONE_remove_input);
// Remove all the X events
// @CLEANUP: Should maybe be done last in the frame
swiss_resetComponent(&ps->win_list, COMPONENT_MAP);
swiss_resetComponent(&ps->win_list, COMPONENT_UNMAP);
swiss_resetComponent(&ps->win_list, COMPONENT_MOVE);
swiss_removeComponentWhere(
&ps->win_list,
COMPONENT_RESIZE,
(enum ComponentType[]){COMPONENT_PHYSICAL, CQ_END}
);

swiss_resetComponent(em, COMPONENT_WINTYPE_CHANGE);

swiss_resetComponent(&ps->win_list, COMPONENT_FOCUS_CHANGE);

for_components(it, em, COMPONENT_SHAPE_DAMAGED, CQ_END) {
struct ShapeDamagedEvent* shapeDamaged = swiss_getComponent(em, COMPONENT_SHAPE_DAMAGED, it.id);
if(shapeDamaged->rects.elementSize != 0) {
vector_kill(&shapeDamaged->rects);
}
}
swiss_resetComponent(&ps->win_list, COMPONENT_SHAPE_DAMAGED);

zone_leave(&ZONE_remove_input);


swiss_resetComponent(&ps->win_list, COMPONENT_CONTENTS_DAMAGED);

Expand Down
119 changes: 119 additions & 0 deletions src/debug.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#include "debug.h"

#include "text.h"
#include "window.h"

static Vector2 X11_rectpos_to_gl(const Vector2* rootSize, const Vector2* xpos, const Vector2* size) {
Vector2 glpos = {{
xpos->x, rootSize->y - xpos->y - size->y
}};
return glpos;
}

char *component_names[NUM_COMPONENT_TYPES] = {
"[Meta]",
"[Mud]",
"[Physical]",
"[Z]",
"[Binds Texture]",
"[Textured]",
"[Tracks Window]",
"[Has Client]",
"[Shadow]",
"[Blur]",
"[Tint]",
"[Opacity]",
"[Fades Opacity]",
"[Dim]",
"[Fades Dim]",
"[Redirected]",
"[Shaped]",
"[Stateful]",
"[Debugged]",

"[Map]",
"[Unmap]",
"[Destroy]",
"[Move]",
"[Resize]",
"[Blur Damaged]",
"[Content Damaged]",
"[Shadow Damaged]",
"[Shape Damaged]",
"[Focus Changed]",
"[Wintype Changed]",
};

static void draw_generic_component(Swiss* em, enum ComponentType ctype) {
Vector2 scale = {{1, 1}};
char buffer[128];

for_components(it, em,
COMPONENT_DEBUGGED, ctype, CQ_END) {
struct DebuggedComponent* debug = swiss_getComponent(em, COMPONENT_DEBUGGED, it.id);

snprintf(buffer, 128, "%s", component_names[ctype]);

Vector2 size = {{0}};
text_size(&debug_font, buffer, &scale, &size);
debug->pen.y -= size.y;

text_draw(&debug_font, buffer, &debug->pen, &scale);
}
}

static void draw_state_component(Swiss* em, enum ComponentType ctype) {
Vector2 scale = {{1, 1}};
char buffer[128];

for_components(it, em,
COMPONENT_DEBUGGED, COMPONENT_STATEFUL, CQ_END) {
struct DebuggedComponent* debug = swiss_getComponent(em, COMPONENT_DEBUGGED, it.id);

snprintf(buffer, 128, "%s", component_names[ctype]);

Vector2 size = {{0}};
text_size(&debug_font, buffer, &scale, &size);
debug->pen.y -= size.y;

text_draw(&debug_font, buffer, &debug->pen, &scale);
}

for_components(it, em,
COMPONENT_DEBUGGED, COMPONENT_STATEFUL, CQ_END) {
struct DebuggedComponent* debug = swiss_getComponent(em, COMPONENT_DEBUGGED, it.id);
struct StatefulComponent* state = swiss_getComponent(em, COMPONENT_STATEFUL, it.id);

snprintf(buffer, 128, " %s", StateNames[state->state]);

Vector2 size = {{0}};
text_size(&debug_font, buffer, &scale, &size);
debug->pen.y -= size.y;

text_draw(&debug_font, buffer, &debug->pen, &scale);
}
}

typedef void (*debug_component_renderer)(Swiss* em, enum ComponentType ctype);
debug_component_renderer component_renderer[NUM_COMPONENT_TYPES] = {
0,
[COMPONENT_STATEFUL] = draw_state_component,
};

void draw_component_debug(Swiss* em, Vector2* rootSize) {
for_components(it, em,
COMPONENT_DEBUGGED, COMPONENT_PHYSICAL, CQ_END) {
struct DebuggedComponent* debug = swiss_getComponent(em, COMPONENT_DEBUGGED, it.id);
struct PhysicalComponent* physical = swiss_getComponent(em, COMPONENT_PHYSICAL, it.id);
Vector2 winPos = X11_rectpos_to_gl(rootSize, &physical->position, &physical->size);
debug->pen = (Vector2){{winPos.x, winPos.y + physical->size.y - 20}};
}

for(int i = 0; i < NUM_COMPONENT_TYPES; i++) {
debug_component_renderer renderer = component_renderer[i];
if(renderer == NULL)
draw_generic_component(em, i);
else
renderer(em, i);
}
}
6 changes: 6 additions & 0 deletions src/debug.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once

#include "vmath.h"
#include "swiss.h"

void draw_component_debug(Swiss* em, Vector2* rootSize);

0 comments on commit 0281033

Please sign in to comment.