Code Monkey home page Code Monkey logo

Comments (4)

chjj avatar chjj commented on August 19, 2024

@richardgv, thanks for the good work again. I feel really uneasy about performance hits. I want to leave this issue open for now. This was honestly the most minor of the 3 major bugs.

I've made you a core committer on compton. Ideally it would be good to discuss changes through pull requests, but you have commit access if you need it. Just be careful, a lot of packages point to this repo as a release since I don't do formal releases for compton.

And I hope you won't simply stop updating compton after the 3 issues get resolved.

I plan to maintain compton for as long as I can. It's just the past few months have been busy for me.

Change paint_all() so it paints in a different sequence: Paints the root window, paint the lowest window's shadow on it, paint the lowest window, paint the shadow of the second-lowest window... It looks a lot more intuitive, and it should be much easier to implement, perhaps helpful in performance. I'm afraid, nonetheless, these xcompmgr developers, with infinite wisdom, might choose the current seemingly-clumsy painting method on purpose. I believe it's the most promising fix, and it requires most code modification.

It might be interesting to start up a separate branch for this. I'm all for rewriting everything honestly. If we can make it better, let's do it.

from compton.

richardgv avatar richardgv commented on August 19, 2024

I feel really uneasy about performance hits. I want to leave this issue open for now. This was honestly the most minor of the 3 major bugs.

If you need a quick fix right now, my fix 2 combined with fix 8 shouldn't cause too aggressive a performance hit.

I looked into the source code of libXfixes and xorg-server, and seemingly the rounding shapes are returned by the program creating the window. Chromium has quite a huge amount of code, looks like ./ui/aura/root_window_host_linux.cc contains the code that handles the window, I'm still searching for where exactly the bounding shapes are added and modified. Maybe it's better to ask Chromium devs firstly before I work on a patch to Chromium?

Update: chrome/browser/ui/gtk/rounded_window.cc andchrome/browser/ui/gtk/browser_window_gtk.cc may actually be the key files.

I've made you a core committer on compton. Ideally it would be good to discuss changes through pull requests, but you have commit access if you need it. Just be careful, a lot of packages point to this repo as a release since I don't do formal releases for compton.

I usually won't commit without prior discussion, unless my GitHub account is hacked, I assume.

I personally believe compton is stable enough for a formal release if it could be verified that my recent patches don't do something awful. With a formal release it helps to keep the snapshots of various distros in sync. Right now at least Fedora and Gentoo come with snapshots of different dates in their official repo.

I plan to maintain compton for as long as I can. It's just the past few months have been busy for me.

Awesome.

It might be interesting to start up a separate branch for this. I'm all for rewriting everything honestly. If we can make it better, let's do it.

xcompmgr has quite some ugly code that got inherited to compton, indeed. I will see what I can help.

from compton.

richardgv avatar richardgv commented on August 19, 2024

In Chromium's chrome/browser/ui/gtk/browser_window_gtk.cc I found the code to update the bounding region:

void BrowserWindowGtk::UpdateWindowShape(int width, int height) {
  GdkRegion* mask = GetWindowShape(width, height);
  gdk_window_shape_combine_region(
      gtk_widget_get_window(GTK_WIDGET(window_)), mask, 0, 0);
  if (mask)
    gdk_region_destroy(mask);

  if (UseCustomFrame() && !IsFullscreen() && !IsMaximized()) {
    gtk_alignment_set_padding(GTK_ALIGNMENT(window_container_), 1,
        kFrameBorderThickness, kFrameBorderThickness, kFrameBorderThickness);
  } else {
    gtk_alignment_set_padding(GTK_ALIGNMENT(window_container_), 0, 0, 0, 0);
  }
}

GdkRegion* BrowserWindowGtk::GetWindowShape(int width, int height) const {
  if (UseCustomFrame() && !IsFullscreen() && !IsMaximized()) {
    // Make the corners rounded.  We set a mask that includes most of the
    // window except for a few pixels in each corner.
    GdkRectangle top_top_rect = { 3, 0, width - 6, 1 };
    GdkRectangle top_mid_rect = { 1, 1, width - 2, 2 };
    GdkRectangle mid_rect = { 0, 3, width, height - 6 };
    // The bottom two rects are mirror images of the top two rects.
    GdkRectangle bot_mid_rect = top_mid_rect;
    bot_mid_rect.y = height - 3;
    GdkRectangle bot_bot_rect = top_top_rect;
    bot_bot_rect.y = height - 1;
    GdkRegion* mask = gdk_region_rectangle(&top_top_rect);
    gdk_region_union_with_rect(mask, &top_mid_rect);
    gdk_region_union_with_rect(mask, &mid_rect);
    gdk_region_union_with_rect(mask, &bot_mid_rect);
    gdk_region_union_with_rect(mask, &bot_bot_rect);
    return mask;
  } else if (UseCustomFrame()) {
    // Disable rounded corners.  Simply passing in a NULL region doesn't
    // seem to work on KWin, so manually set the shape to the whole window.
    GdkRectangle rect = { 0, 0, width, height };
    GdkRegion* mask = gdk_region_rectangle(&rect);
    return mask;
  } else {
    // XFCE disables the system decorations if there's an xshape set. Do not
    // use the KWin hack when the custom frame is not enabled.
    return NULL;
  }
}

UpdateWindowShape() is called upon window changes, theme changes, etc. Unfortunately, I didn't find a way to update the bounding region before the window geometry changes -- I doubt if it's possible, actually, considering the design of X. I've reported this to Chromium, however I guess it's questionable whether they will fix it somehow or provide a workaround: https://code.google.com/p/chromium/issues/detail?id=147533

from compton.

richardgv avatar richardgv commented on August 19, 2024

Sorry that I didn't know there's a thing called ShapeNotify previously. I'm able to write a patch with it that probably solved these two issues with minimal performance impact:

(Note this patched included and improved my "better debug" patch as I needed to include event debugging support for Shape extension, and don't feel like pushing out another patch just for "better debug with ShapeNotify support".)

diff --git a/Makefile b/Makefile
index b0c9d5e..60a5c7a 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ PREFIX ?= /usr
 BINDIR ?= $(PREFIX)/bin
 MANDIR ?= $(PREFIX)/share/man/man1

-PACKAGES = x11 xcomposite xfixes xdamage xrender
+PACKAGES = x11 xcomposite xfixes xdamage xrender xext
 LIBS = $(shell pkg-config --libs $(PACKAGES)) -lm
 INCS = $(shell pkg-config --cflags $(PACKAGES))
 CFLAGS += -Wall
diff --git a/src/compton.c b/src/compton.c
index b72dc59..7d66ec1 100644
--- a/src/compton.c
+++ b/src/compton.c
@@ -10,6 +10,10 @@

 #include "compton.h"

+#if DEBUG_EVENTS
+static int window_get_name(Window w, char **name);
+#endif
+
 /**
  * Shared
  */
@@ -36,6 +40,10 @@ ignore *ignore_head, **ignore_tail = &ignore_head;
 int xfixes_event, xfixes_error;
 int damage_event, damage_error;
 int composite_event, composite_error;
+/// Whether the Shape extension exists
+Bool shape_exists = True;
+/// event_base and error_base for Shape extension
+int shape_event, shape_error;
 int render_event, render_error;
 int composite_opcode;

@@ -954,12 +962,7 @@ paint_all(Display *dpy, XserverRegion region) {
   win *t = 0;

   if (!region) {
-    XRectangle r;
-    r.x = 0;
-    r.y = 0;
-    r.width = root_width;
-    r.height = root_height;
-    region = XFixesCreateRegion(dpy, &r, 1);
+    region = get_screen_region(dpy);
   }

 #if MONITOR_REPAINT
@@ -1029,11 +1032,7 @@ paint_all(Display *dpy, XserverRegion region) {
 #endif

     if (clip_changed) {
-      if (w->border_size) {
-        set_ignore(dpy, NextRequest(dpy));
-        XFixesDestroyRegion(dpy, w->border_size);
-        w->border_size = None;
-      }
+      win_free_border_size(dpy, w);
       if (w->extents) {
         XFixesDestroyRegion(dpy, w->extents);
         w->extents = None;
@@ -1372,6 +1371,10 @@ map_win(Display *dpy, Window id,
      so that no property changes are lost */
   if (!override_redirect) {
     XSelectInput(dpy, id, PropertyChangeMask | FocusChangeMask);
+    // Notify compton when the shape of a window changes
+    if (shape_exists) {
+      XShapeSelectInput(dpy, id, ShapeNotifyMask);
+    }
   }

   // this causes problems for inactive transparency
@@ -1425,11 +1428,7 @@ finish_unmap_win(Display *dpy, win *w) {
     w->picture = None;
   }

-  if (w->border_size) {
-    set_ignore(dpy, NextRequest(dpy));
-    XFixesDestroyRegion(dpy, w->border_size);
-    w->border_size = None;
-  }
+  win_free_border_size(dpy, w);

   if (w->shadow) {
     XRenderFreePicture(dpy, w->shadow);
@@ -1692,6 +1691,33 @@ restack_win(Display *dpy, win *w, Window new_above) {

     w->next = *prev;
     *prev = w;
+
+#if DEBUG_RESTACK
+    {
+      const char *desc;
+      char *window_name;
+      Bool to_free;
+      win* c = list;
+  
+      printf("restack_win(%#010lx, %#010lx): Window stack modified. Current stack:\n", w->id, new_above);
+      for (; c; c = c->next) {
+        window_name = "(Failed to get title)";
+        if (root == c->id)
+          window_name = "(Root window)";
+        else
+          to_free = window_get_name(c->id, &window_name);
+        desc = "";
+        if (c->destroyed)
+          desc = "(D) ";
+        printf("%#010lx \"%s\" %s-> ", c->id, window_name, desc);
+        if (to_free) {
+          XFree(window_name);
+          window_name = NULL;
+        }
+      }
+      fputs("\n", stdout);
+    }
+#endif
   }
 }

@@ -1957,6 +1983,60 @@ error(Display *dpy, XErrorEvent *ev) {
       break;
   }

+  switch (ev->error_code) {
+    case BadAccess:
+      name = "BadAccess";
+      break;
+    case BadAlloc:
+      name = "BadAlloc";
+      break;
+    case BadAtom:
+      name = "BadAtom";
+      break;
+    case BadColor:
+      name = "BadColor";
+      break;
+    case BadCursor:
+      name = "BadCursor";
+      break;
+    case BadDrawable:
+      name = "BadDrawable";
+      break;
+    case BadFont:
+      name = "BadFont";
+      break;
+    case BadGC:
+      name = "BadGC";
+      break;
+    case BadIDChoice:
+      name = "BadIDChoice";
+      break;
+    case BadImplementation:
+      name = "BadImplementation";
+      break;
+    case BadLength:
+      name = "BadLength";
+      break;
+    case BadMatch:
+      name = "BadMatch";
+      break;
+    case BadName:
+      name = "BadName";
+      break;
+    case BadPixmap:
+      name = "BadPixmap";
+      break;
+    case BadRequest:
+      name = "BadRequest";
+      break;
+    case BadValue:
+      name = "BadValue";
+      break;
+    case BadWindow:
+      name = "BadWindow";
+      break;
+  }
+
   printf("error %d (%s) request %d minor %d serial %lu\n",
     ev->error_code, name, ev->request_code,
     ev->minor_code, ev->serial);
@@ -1970,10 +2050,36 @@ expose_root(Display *dpy, Window root, XRectangle *rects, int nrects) {
   add_damage(dpy, region);
 }

-#if DEBUG_EVENTS
+#if DEBUG_EVENTS || DEBUG_RESTACK
+static int window_get_name(Window w, char **name) {
+  Atom prop = XInternAtom(dpy, "_NET_WM_NAME", False);
+  Atom utf8_type = XInternAtom(dpy, "UTF8_STRING", False);
+  Atom actual_type;
+  int actual_format;
+  unsigned long nitems;
+  unsigned long leftover;
+  char *data = NULL;
+  Status ret;
+
+  set_ignore(dpy, NextRequest(dpy));
+  if (Success != (ret = XGetWindowProperty(dpy, w, prop, 0L, (long) BUFSIZ,
+        False, utf8_type, &actual_type, &actual_format, &nitems,
+        &leftover, (unsigned char **) &data))) {
+    if (BadWindow == ret)
+      return 0;
+   set_ignore(dpy, NextRequest(dpy));
+    printf("Window %#010lx: _NET_WM_NAME unset, falling back to WM_NAME.\n", w);
+    if (!XFetchName(dpy, w, &data))
+      return 0;
+  }
+  // if (actual_type == utf8_type && actual_format == 8)
+  *name = (char *) data;
+  return 1;
+}
+
 static int
 ev_serial(XEvent *ev) {
-  if (ev->type & 0x7f != KeymapNotify) {
+  if ((ev->type & 0x7f) != KeymapNotify) {
     return ev->xany.serial;
   }
   return NextRequest(ev->xany.display);
@@ -1983,8 +2089,16 @@ static char *
 ev_name(XEvent *ev) {
   static char buf[128];
   switch (ev->type & 0x7f) {
-    case Expose:
-      return "Expose";
+    case FocusIn:
+      return "FocusIn";
+    case FocusOut:
+      return "FocusOut";
+    case CreateNotify:
+      return "CreateNotify";
+    case ConfigureNotify:
+      return "ConfigureNotify";
+    case DestroyNotify:
+      return "DestroyNotify";
     case MapNotify:
       return "Map";
     case UnmapNotify:
@@ -1993,10 +2107,16 @@ ev_name(XEvent *ev) {
       return "Reparent";
     case CirculateNotify:
       return "Circulate";
+    case Expose:
+      return "Expose";
+    case PropertyNotify:
+      return "PropertyNotify";
     default:
       if (ev->type == damage_event + XDamageNotify) {
         return "Damage";
       }
+      if (shape_exists && ev->type == shape_event)
+        return "ShapeNotify";
       sprintf(buf, "Event %d", ev->type);
       return buf;
   }
@@ -2005,8 +2125,13 @@ ev_name(XEvent *ev) {
 static Window
 ev_window(XEvent *ev) {
   switch (ev->type) {
-    case Expose:
-      return ev->xexpose.window;
+    case FocusIn:
+    case FocusOut:
+      return ev->xfocus.window;
+    case CreateNotify:
+      return ev->xcreatewindow.window;
+    case ConfigureNotify:
+      return ev->xconfigure.window;
     case MapNotify:
       return ev->xmap.window;
     case UnmapNotify:
@@ -2015,10 +2140,16 @@ ev_window(XEvent *ev) {
       return ev->xreparent.window;
     case CirculateNotify:
       return ev->xcirculate.window;
+    case Expose:
+      return ev->xexpose.window;
+    case PropertyNotify:
+      return ev->xproperty.window;
     default:
       if (ev->type == damage_event + XDamageNotify) {
         return ((XDamageNotifyEvent *)ev)->drawable;
       }
+      if (shape_exists && ev->type == shape_event)
+        return ((XShapeEvent *) ev)->window;
       return 0;
   }
 }
@@ -2064,6 +2195,9 @@ ev_create_notify(XCreateWindowEvent *ev) {

 inline static void
 ev_configure_notify(XConfigureEvent *ev) {
+#if DEBUG_EVENTS
+  printf("{ send_event: %d, above: %08lx, override_redirect: %d }\n", ev->send_event, ev->above, ev->override_redirect);
+#endif
   configure_win(dpy, ev);
 }

@@ -2164,16 +2298,56 @@ ev_damage_notify(XDamageNotifyEvent *ev) {
   damage_win(dpy, ev);
 }

+static void ev_shape_notify(XShapeEvent *ev) {
+  win *w = find_win(dpy, ev->window);
+
+  /*
+   * Empty border size may indicated an
+   * unmapped/destoried window, in which case
+   * seemingly BadRegion errors would be triggered
+   * if we attempt to rebuild border_size
+   */
+  if (w->border_size) {
+    // Mark the old border_size as damaged
+    add_damage(dpy, w->border_size);
+
+    w->border_size = border_size(dpy, w);
+
+    // Mark the new border_size as damaged
+    add_damage(dpy, copy_region(dpy, w->border_size));
+  }
+
+}
+
 inline static void
 ev_handle(XEvent *ev) {
+
+#if DEBUG_EVENTS
+  Window w;
+  char *window_name;
+  Bool to_free = False;
+#endif
+
   if ((ev->type & 0x7f) != KeymapNotify) {
     discard_ignore(dpy, ev->xany.serial);
   }

 #if DEBUG_EVENTS
+  w = ev_window(ev);
+  window_name = "(Failed to get title)";
+  if (w) {
+    if (root == w)
+      window_name = "(Root window)";
+    else
+      to_free = window_get_name(w, &window_name);
+  }
   if (ev->type != damage_event + XDamageNotify) {
-    printf("event %10.10s serial 0x%08x window 0x%08x\n",
-      ev_name(ev), ev_serial(ev), ev_window(ev));
+    printf("event %10.10s serial %#010x window %#010lx \"%s\"\n",
+      ev_name(ev), ev_serial(ev), w, window_name);
+  }
+  if (to_free) {
+    XFree(window_name);
+    window_name = NULL;
   }
 #endif

@@ -2212,6 +2386,10 @@ ev_handle(XEvent *ev) {
       ev_property_notify((XPropertyEvent *)ev);
       break;
     default:
+    if (shape_exists && ev->type == shape_event) {
+      ev_shape_notify((XShapeEvent *) ev);
+      break;
+    }
       if (ev->type == damage_event + XDamageNotify) {
         ev_damage_notify((XDamageNotifyEvent *)ev);
       }
@@ -2524,6 +2702,9 @@ main(int argc, char **argv) {
     exit(1);
   }

+  if (!XShapeQueryExtension(dpy, &shape_event, &shape_error))
+    shape_exists = False;
+
   register_cm(scr);

   if (fork_after_register) fork_after();
diff --git a/src/compton.h b/src/compton.h
index 18e3859..6f30391 100644
--- a/src/compton.h
+++ b/src/compton.h
@@ -20,6 +20,7 @@
 #include <X11/extensions/Xcomposite.h>
 #include <X11/extensions/Xdamage.h>
 #include <X11/extensions/Xrender.h>
+#include <X11/extensions/shape.h>

 #if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2
 #define HAS_NAME_WINDOW_PIXMAP 1
@@ -28,6 +29,7 @@
 #define CAN_DO_USABLE 0
 #define DEBUG_REPAINT 0
 #define DEBUG_EVENTS 0
+#define DEBUG_RESTACK 0
 #define DEBUG_WINTYPE 0
 #define MONITOR_REPAINT 0

@@ -124,6 +126,7 @@ typedef struct _fade {
   Display *dpy;
 } fade;

+extern int root_height, root_width;
 /**
  * Functions
  */
@@ -345,6 +348,42 @@ ev_property_notify(XPropertyEvent *ev);
 inline static void
 ev_damage_notify(XDamageNotifyEvent *ev);

+/**
+ * Destory the cached border_size of a window.
+ */
+inline static void win_free_border_size(Display *dpy, win *w) {
+  if (w->border_size) {
+    set_ignore(dpy, NextRequest(dpy));
+    XFixesDestroyRegion(dpy, w->border_size);
+    w->border_size = None;
+  }
+}
+
+/**
+ * Get a region of the screen size.
+ */
+inline static XserverRegion get_screen_region(Display *dpy) {
+  XRectangle r;
+
+  r.x = 0;
+  r.y = 0;
+  r.width = root_width;
+  r.height = root_height;
+  return XFixesCreateRegion(dpy, &r, 1);
+}
+
+/**
+ * Copies a region
+ */
+inline static XserverRegion copy_region(Display *dpy,
+    XserverRegion oldregion) {
+  XserverRegion region = XFixesCreateRegion(dpy, NULL, 0);
+
+  XFixesCopyRegion(dpy, region, oldregion);
+
+  return region;
+}
+
 inline static void
 ev_handle(XEvent *ev);

It adds another dependency, libXext, for the X Shape extension function calls, which I suppose every sane X user has installed. I tried to make it work without X Shape extension, yet I did not test the support.

As the issues are tied to timing, I could not state that this patch absolutely resolves the issues. I could only confirm that I tried 30+ times without being able to reproduce the issue.

I personally use DoxyGen comment format. Change it to other styles if you don't like it.

A depressing news is, I rewrote paint_all() to adapt the new painting sequence, and so far it does not render shaped windows correctly if I don't use the bounding regions. Much annoyance with how XRenderCreatePicture() and XRenderComposite() works. I'm still looking into the exact reasons.

By the way, I discovered two possible bugs in compton:

  1. If you set transparency of a window before compton starts running with compton-trans, after you run compton the window is rendered solid, i.e. compton-trans looks only effective for compton when it's running. xcompmgr respects transparency set when it's not running;
  2. When you make a specially shaped window (I triggered it with oclock) transparent, compton will render the completely tranparent parts (the corners of an oclock window, for example) incorrectly. xcompmgr doesn't show anything wrong. It's probably related to why my rewritting paint_all() does not work.

I don't have to time to look at them today. Too sleepy.

from compton.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.