Fix races that occur when a window id is destroyed and recreated very quickly. This...
[dana/xcompmgr.git] / xcompmgr.c
index 8f7a77b..276d1bd 100644 (file)
@@ -100,6 +100,10 @@ typedef struct _win {
     unsigned int       opacity;
     wintype             windowType;
     unsigned long      damage_sequence;    /* sequence when damage was created */
+    Bool                destroyed;
+
+    Bool                need_configure;
+    XConfigureEvent     queue_configure;
 
     /* for drawing translucent windows */
     XserverRegion      borderClip;
@@ -155,6 +159,7 @@ Bool            winTypeFade[NUM_WINTYPES];
 
 /* opacity property name; sometime soon I'll write up an EWMH spec for it */
 #define OPACITY_PROP   "_NET_WM_WINDOW_OPACITY"
+#define REGISTER_PROP   "_NET_WM_CM_S"
 
 #define TRANSLUCENT    0xe0000000
 #define OPAQUE         0xffffffff
@@ -308,8 +313,19 @@ set_fade (Display *dpy, win *w, double start, double finish, double step,
     {
        XRenderFreePicture (dpy, w->shadow);
        w->shadow = None;
-       w->extents = win_extents (dpy, w);
+
+        if (w->extents != None)
+            XFixesDestroyRegion (dpy, w->extents);
+
+        /* rebuild the shadow */
+        w->extents = win_extents (dpy, w);
     }
+
+    /* fading windows need to be drawn, mark them as damaged.
+       when a window maps, if it tries to fade in but it already at the right
+       opacity (map/unmap/map fast) then it will never get drawn without this
+       until it repaints */
+    w->damaged = 1;
 }
 
 int
@@ -378,6 +394,11 @@ run_fades (Display *dpy)
        {
            XRenderFreePicture (dpy, w->shadow);
            w->shadow = None;
+
+            if (w->extents != None)
+                XFixesDestroyRegion (dpy, w->extents);
+
+            /* rebuild the shadow */
            w->extents = win_extents(dpy, w);
        }
        /* Must do this last as it might destroy f->w in callbacks */
@@ -759,7 +780,7 @@ find_win (Display *dpy, Window id)
     win        *w;
 
     for (w = list; w; w = w->next)
-       if (w->id == id)
+       if (w->id == id && !w->destroyed)
            return w;
     return 0;
 }
@@ -1280,6 +1301,9 @@ static unsigned int
 get_opacity_prop (Display *dpy, win *w, unsigned int def);
 
 static void
+configure_win (Display *dpy, XConfigureEvent *ce);
+
+static void
 map_win (Display *dpy, Window id, unsigned long sequence, Bool fade)
 {
     win                *w = find_win (dpy, id);
@@ -1288,38 +1312,31 @@ map_win (Display *dpy, Window id, unsigned long sequence, Bool fade)
        return;
 
     w->a.map_state = IsViewable;
-    
-    /* This needs to be here or else we lose transparency messages */
-    XSelectInput (dpy, id, PropertyChangeMask);
-
-    /* This needs to be here since we don't get PropertyNotify when unmapped */
-    w->opacity = get_opacity_prop (dpy, w, OPAQUE);
-    determine_mode (dpy, w);
 
     w->windowType = determine_wintype (dpy, w->id, w->id);
 #if 0
     printf("window 0x%x type %s\n", w->id, wintype_name(w->windowType));
 #endif
 
+    /* select before reading the property so that no property changes are lost */
+    XSelectInput (dpy, id, PropertyChangeMask);
+    w->opacity = get_opacity_prop (dpy, w, OPAQUE);
+
+    determine_mode (dpy, w);
+
 #if CAN_DO_USABLE
     w->damage_bounds.x = w->damage_bounds.y = 0;
     w->damage_bounds.width = w->damage_bounds.height = 0;
 #endif
     w->damaged = 0;
 
-#if HAS_NAME_WINDOW_PIXMAP
-    /* If the window was previously mapped and its pixmap still exists, it
-       is out of date now, so force us to reacquire it.  (If the window
-       re-maps before the unmap fade-out finished) */
-    if (w->pixmap)
-    {
-        XFreePixmap (dpy, w->pixmap);
-        w->pixmap = None;
-    }
-#endif
-
     if (fade && winTypeFade[w->windowType])
        set_fade (dpy, w, 0, get_opacity_percent (dpy, w), fade_in_step, 0, True, True);
+
+    /* if any configure events happened while the window was unmapped, then
+       configure the window to its correct place */
+    if (w->need_configure)
+        configure_win (dpy, &w->queue_configure);
 }
 
 static void
@@ -1350,10 +1367,6 @@ finish_unmap_win (Display *dpy, win *w)
        w->picture = None;
     }
 
-    /* don't care about properties anymore */
-    set_ignore (dpy, NextRequest (dpy));
-    XSelectInput(dpy, w->id, 0);
-
     if (w->borderSize)
     {
        set_ignore (dpy, NextRequest (dpy));
@@ -1389,6 +1402,11 @@ unmap_win (Display *dpy, Window id, Bool fade)
     if (!w)
        return;
     w->a.map_state = IsUnmapped;
+
+    /* don't care about properties anymore */
+    set_ignore (dpy, NextRequest (dpy));
+    XSelectInput(dpy, w->id, 0);
+
 #if HAS_NAME_WINDOW_PIXMAP
     if (w->pixmap && fade && winTypeFade[w->windowType])
        set_fade (dpy, w, w->opacity*1.0/OPAQUE, 0.0, fade_out_step, unmap_callback, False, True);
@@ -1496,7 +1514,7 @@ add_win (Display *dpy, Window id, Window prev)
     if (prev)
     {
        for (p = &list; *p; p = &(*p)->next)
-           if ((*p)->id == prev)
+           if ((*p)->id == prev && !(*p)->destroyed)
                break;
     }
     else
@@ -1536,6 +1554,8 @@ add_win (Display *dpy, Window id, Window prev)
     new->shadow_width = 0;
     new->shadow_height = 0;
     new->opacity = OPAQUE;
+    new->destroyed = False;
+    new->need_configure = False;
 
     new->borderClip = None;
     new->prev_trans = 0;
@@ -1568,7 +1588,7 @@ restack_win (Display *dpy, win *w, Window new_above)
        /* rehook */
        for (prev = &list; *prev; prev = &(*prev)->next)
        {
-           if ((*prev)->id == new_above)
+           if ((*prev)->id == new_above && !(*prev)->destroyed)
                break;
        }
        w->next = *prev;
@@ -1596,50 +1616,59 @@ configure_win (Display *dpy, XConfigureEvent *ce)
        }
        return;
     }
+
+    if (w->a.map_state == IsUnmapped) {
+        /* save the configure event for when the window maps */
+        w->need_configure = True;
+        w->queue_configure = *ce;
+    }
+    else {
+        w->need_configure = False;
+
 #if CAN_DO_USABLE
-    if (w->usable)
+        if (w->usable)
 #endif
-    {
-       damage = XFixesCreateRegion (dpy, 0, 0);
-       if (w->extents != None) 
-           XFixesCopyRegion (dpy, damage, w->extents);
-    }
-    w->a.x = ce->x;
-    w->a.y = ce->y;
-    /* Only destroy the pixmap if the window is mapped */
-    if (w->a.map_state != IsUnmapped &&
-        (w->a.width != ce->width || w->a.height != ce->height))
-    {
+        {
+            damage = XFixesCreateRegion (dpy, 0, 0);
+            if (w->extents != None)
+                XFixesCopyRegion (dpy, damage, w->extents);
+        }
+
+        w->a.x = ce->x;
+        w->a.y = ce->y;
+        if (w->a.width != ce->width || w->a.height != ce->height)
+        {
 #if HAS_NAME_WINDOW_PIXMAP
-       if (w->pixmap)
-       {
-           XFreePixmap (dpy, w->pixmap);
-           w->pixmap = None;
-           if (w->picture)
-           {
-               XRenderFreePicture (dpy, w->picture);
-               w->picture = None;
-           }
-       }
+            if (w->pixmap)
+            {
+                XFreePixmap (dpy, w->pixmap);
+                w->pixmap = None;
+                if (w->picture)
+                {
+                    XRenderFreePicture (dpy, w->picture);
+                    w->picture = None;
+                }
+            }
 #endif
-       if (w->shadow)
-       {
-           XRenderFreePicture (dpy, w->shadow);
-           w->shadow = None;
-       }
+            if (w->shadow)
+            {
+                XRenderFreePicture (dpy, w->shadow);
+                w->shadow = None;
+            }
+        }
+        w->a.width = ce->width;
+        w->a.height = ce->height;
+        w->a.border_width = ce->border_width;
+        if (w->a.map_state != IsUnmapped && damage)
+        {
+            XserverRegion      extents = win_extents (dpy, w);
+            XFixesUnionRegion (dpy, damage, damage, extents);
+            XFixesDestroyRegion (dpy, extents);
+            add_damage (dpy, damage);
+        }
     }
-    w->a.width = ce->width;
-    w->a.height = ce->height;
-    w->a.border_width = ce->border_width;
     w->a.override_redirect = ce->override_redirect;
     restack_win (dpy, w, ce->above);
-    if (w->a.map_state != IsUnmapped && damage)
-    {
-       XserverRegion   extents = win_extents (dpy, w);
-       XFixesUnionRegion (dpy, damage, damage, extents);
-       XFixesDestroyRegion (dpy, extents);
-       add_damage (dpy, damage);
-    }
     clipChanged = True;
 }
 
@@ -1666,7 +1695,7 @@ finish_destroy_win (Display *dpy, Window id)
     win        **prev, *w;
 
     for (prev = &list; (w = *prev); prev = &w->next)
-       if (w->id == id)
+       if (w->id == id && w->destroyed)
        {
             finish_unmap_win (dpy, w);
            *prev = w->next;
@@ -1686,6 +1715,7 @@ finish_destroy_win (Display *dpy, Window id)
                XDamageDestroy (dpy, w->damage);
                w->damage = None;
            }
+
            cleanup_fade (dpy, w);
            free (w);
            break;
@@ -1704,6 +1734,9 @@ static void
 destroy_win (Display *dpy, Window id, Bool fade)
 {
     win *w = find_win (dpy, id);
+
+    if (w) w->destroyed = True;
+
 #if HAS_NAME_WINDOW_PIXMAP
     if (w && w->pixmap && fade && winTypeFade[w->windowType])
        set_fade (dpy, w, w->opacity*1.0/OPAQUE, 0.0, fade_out_step,
@@ -1925,10 +1958,14 @@ usage (char *program)
 }
 
 static void
-register_cm (void)
+register_cm (int scr)
 {
     Window w;
     Atom a;
+    char *buf;
+    int len, s;
+
+    if (scr < 0) return;
 
     w = XCreateSimpleWindow (dpy, RootWindow (dpy, 0), 0, 0, 1, 1, 0, None,
                             None);
@@ -1936,8 +1973,17 @@ register_cm (void)
     Xutf8SetWMProperties (dpy, w, "xcompmgr", "xcompmgr", NULL, 0, NULL, NULL,
                          NULL);
 
-    /* FIXME: Don't hard code the screen number */
-    a = XInternAtom (dpy, "_NET_WM_CM_S0", False);
+    len = strlen(REGISTER_PROP) + 2;
+    s = scr;
+    while (s >= 10) {
+        ++len;
+        s /= 10;
+    }
+    buf = malloc(len);
+    snprintf(buf, len, REGISTER_PROP"%d", scr);
+
+    a = XInternAtom (dpy, buf, False);
+    free(buf);
 
     XSetSelectionOwner (dpy, a, w, 0);
 }
@@ -2087,7 +2133,7 @@ main (int argc, char **argv)
        exit (1);
     }
 
-    register_cm();
+    register_cm(scr);
 
     /* get atoms */
     opacityAtom = XInternAtom (dpy, OPACITY_PROP, False);
@@ -2253,12 +2299,17 @@ main (int argc, char **argv)
                                      fade_out_step, 0, True, False);
                        else
                        {
-                       w->opacity = get_opacity_prop(dpy, w, OPAQUE);
-                       determine_mode(dpy, w);
+                            w->opacity = get_opacity_prop(dpy, w, OPAQUE);
+                            determine_mode(dpy, w);
                            if (w->shadow)
                            {
                                XRenderFreePicture (dpy, w->shadow);
                                w->shadow = None;
+
+                                if (w->extents != None)
+                                    XFixesDestroyRegion (dpy, w->extents);
+
+                                /* rebuild the shadow */
                                w->extents = win_extents (dpy, w);
                            }
                        }