├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cm-global.c ├── cm-global.h ├── cm-root.c ├── cm-root.h ├── cm-util.c ├── cm-util.h ├── comp_rect.c ├── comp_rect.h ├── fastcompmgr.1 ├── fastcompmgr.c └── ringbuffer.h /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug in the compositor 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | 17 | **Desktop (please complete the following information):** 18 | - OS (Linux distribution) 19 | - Display and driver: output of `inxi -G`: 20 | - version or commit of fastcompmgr 21 | 22 | **Additional context** 23 | - does the bug happen without compositor? 24 | - does the bug happen using xcompmgr/compton/picom? 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | Makefile 3 | Makefile.in 4 | aclocal.m4 5 | autom4te.cache 6 | config.h 7 | config.h.in 8 | config.log 9 | config.status 10 | configure 11 | depcomp 12 | install-sh 13 | missing 14 | stamp-h1 15 | fastcompmgr 16 | *.o 17 | *~ 18 | .geany 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | fastcompmgr - a _fast_ compositor for X11 2 | 3 | Based on xcompmgr and compton, originally written by Keith Packard, 4 | with modifications from several contributors (according to the xcompmgr 5 | man page): Matthew Allum, Eric Anholt, Dan Doel, Thomas Luebking, 6 | Matthew Hawn, Ely Levy, Phil Blundell, and Carl Worth. Menu 7 | transparency was implemented by Dana Jansens, many other improvements, 8 | like shadows on argb windows, by Christopher Jeffrey. 9 | 10 | xcompmgr 11 | 12 | Copyright © 2003 Keith Packard 13 | 14 | Permission to use, copy, modify, distribute, and sell this software and its 15 | documentation for any purpose is hereby granted without fee, provided that 16 | the above copyright notice appear in all copies and that both that 17 | copyright notice and this permission notice appear in supporting 18 | documentation, and that the name of Keith Packard not be used in 19 | advertising or publicity pertaining to distribution of the software without 20 | specific, written prior permission. Keith Packard makes no 21 | representations about the suitability of this software for any purpose. It 22 | is provided "as is" without express or implied warranty. 23 | 24 | KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 25 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 26 | EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR 27 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 28 | DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 29 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 30 | PERFORMANCE OF THIS SOFTWARE. 31 | 32 | compton 33 | 34 | Copyright (c) 2011, Christopher Jeffrey (github.com/chjj) 35 | 36 | Permission is hereby granted, free of charge, to any person obtaining a copy 37 | of this software and associated documentation files (the "Software"), to deal 38 | in the Software without restriction, including without limitation the rights 39 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 40 | copies of the Software, and to permit persons to whom the Software is 41 | furnished to do so, subject to the following conditions: 42 | 43 | The above copyright notice and this permission notice shall be included in 44 | all copies or substantial portions of the Software. 45 | 46 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 47 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 48 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 49 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 50 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 51 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 52 | THE SOFTWARE. 53 | 54 | fastcompmgr 55 | 56 | Copyright (c) 2023, Tycho Kirchner (github.com/tycho-kirchner) 57 | 58 | Permission is hereby granted, free of charge, to any person obtaining a copy 59 | of this software and associated documentation files (the "Software"), to deal 60 | in the Software without restriction, including without limitation the rights 61 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 62 | copies of the Software, and to permit persons to whom the Software is 63 | furnished to do so, subject to the following conditions: 64 | 65 | The above copyright notice and this permission notice shall be included in 66 | all copies or substantial portions of the Software. 67 | 68 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 69 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 70 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 71 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 72 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 73 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 74 | THE SOFTWARE. 75 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PACKAGES = x11 xcomposite xfixes xdamage xrender 2 | LIBS = `pkg-config --libs ${PACKAGES}` -lm 3 | INCS = `pkg-config --cflags ${PACKAGES}` 4 | CFLAGS = -Wall -O3 -flto 5 | PREFIX = /usr/local 6 | MANDIR = ${PREFIX}/share/man/man1 7 | 8 | OBJS=fastcompmgr.o comp_rect.o cm-root.o cm-global.o cm-util.o 9 | 10 | .c.o: 11 | $(CC) $(CFLAGS) $(INCS) -c $*.c 12 | 13 | fastcompmgr: $(OBJS) 14 | $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) 15 | 16 | install: fastcompmgr 17 | @mkdir -p "${PREFIX}/bin" 18 | @cp -t "${PREFIX}/bin" fastcompmgr 19 | @mkdir -p "${MANDIR}" 20 | @cp -t "${MANDIR}" fastcompmgr.1 21 | 22 | uninstall: 23 | @rm -f "${PREFIX}/bin/fastcompmgr" 24 | @rm -f "${MANDIR}/fastcompmgr.1" 25 | 26 | clean: 27 | rm -f $(OBJS) fastcompmgr 28 | 29 | .PHONY: uninstall clean 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fastcompmgr 2 | 3 | __fastcompmgr__ is a _fast_ compositor for X, a fork of an early version 4 | of __Compton__ which is a fork of __xcompmgr-dana__ which is a fork 5 | of __xcompmgr__. 6 | 7 | I used to use good old xcompmgr for long, because compton always 8 | felt a bit laggy when moving/resizing windows or kinetic-scrolling 9 | a webpage. Having tested the latest picom-10.2, it seems, things got even 10 | worse (see benchmark below). However, xcompmgr does not draw shadows 11 | on argb windows (e.g. some terminals) and 12 | has several other glitches. That's why I traveled back into 2011, where 13 | this feature was just added, cherry picked some later compton commits 14 | to get rid of spurious segfaults and memleaks and made that version even 15 | faster, based on profiling. 16 | For example, window move- and resize events are limited in their 17 | event-count and rendered at a somewhat fixed framerate, while 18 | scrolling is still done as fast as possible. Occluded windows are not 19 | painted and memory allocations/deallocations are largely avoided, 20 | allowing for faster repaints of the screen. 21 | On the downside, fading is currently broken (I don't use it). Sorry 22 | for that (; 23 | 24 | ## Benchmark 25 | While on my Dell Latitude E5570 window moving, resizing and scrolling 26 | *feels* clearly faster, there are also some numbers to support this 27 | observation. Given a number of stacked chromium-windows, where no window 28 | is fully occluded, I performed the respective operations *by hand*, 29 | so please beware that the benchmark is not very sophisticated. The touchpad 30 | driver `xserver-xorg-input-synaptics` was used which enables for kinetic 31 | scrolling (Wayland anyone?). The following CPU usages were measured: 32 | 33 | | Compositor | move | resize | scroll | 34 | | ------------- | ----- | ------- | ------ | 35 | | fastcompmgr | 6.7% | 4.4% | 1.5% | 36 | | xcompmgr | 7.8% | 4.9% | 1.6% | 37 | | compton | 26.4% | 6.8% | 17.1% | 38 | | picom | 29.3% | 8.1% | 23.1% | 39 | 40 | 41 | Tools were used with the following flags: 42 | ~~~ 43 | (v0.1) fastcompmgr -o 0.4 -r 12 -c -C 44 | (v1.1.8 from Debian 11) xcompmgr -o 0.4 -r 12 -c -C 45 | (v1 from Debian 11) compton --config /dev/null --backend xrender -o 0.4 -r 12 -c -C 46 | (v10.2) picom --config /dev/null --backend xrender -o 0.4 -r 12 -c 47 | 48 | # Calc average using 49 | $ fastcompmgr -o 0.4 -r 12 -c -C & pid=$!; sleep 4; \ 50 | top -b -n 20 -d 0.5 -p $pid | LC_ALL=C awk -v pid=$pid \ 51 | '$1==PID {++PIDCOUNT} $1==pid && PIDCOUNT>1 {print $9}' | \ 52 | datamash mean 1; kill $pid 53 | ~~~ 54 | 55 | 56 | 57 | ## Installation 58 | If you're lazy, just grab the binary from the [release page](https://github.com/tycho-kirchner/fastcompmgr/releases). 59 | 60 | Otherwise, build dependencies are the same as for xcompmgr: 61 | 62 | ### Dependencies: 63 | 64 | * libx11 65 | * libxcomposite 66 | * libxdamage 67 | * libxfixes 68 | * libxrender 69 | * pkg-config 70 | * make 71 | 72 | To build: 73 | 74 | ~~~ bash 75 | $ make 76 | $ make install 77 | ~~~ 78 | 79 | ## Usage 80 | 81 | ~~~ bash 82 | $ fastcompmgr -o 0.4 -r 12 -c -C 83 | ~~~ 84 | All options (currently fading doesn't work): 85 | ~~~ 86 | -d display 87 | Which display should be managed. 88 | -r radius 89 | The blur radius for shadows. (default 12) 90 | -o opacity 91 | The translucency for shadows. (default .75) 92 | -l left-offset 93 | The left offset for shadows. (default -15) 94 | -t top-offset 95 | The top offset for shadows. (default -15) 96 | -m opacity 97 | The opacity for menus. (default 1.0) 98 | -c 99 | Enabled client-side shadows on windows. 100 | -C 101 | Avoid drawing shadows on dock/panel windows. 102 | -i opacity 103 | Opacity of inactive windows. (0.1 - 1.0) 104 | -e opacity 105 | Opacity of window titlebars and borders. (0.1 - 1.0) 106 | --shadow-red value 107 | Red color value of shadow (0.0 - 1.0, defaults to 0). 108 | --shadow-green value 109 | Green color value of shadow (0.0 - 1.0, defaults to 0). 110 | --shadow-blue value 111 | Blue color value of shadow (0.0 - 1.0, defaults to 0). 112 | 113 | ~~~ 114 | 115 | 116 | ## License 117 | 118 | xcompmgr has gotten around. As far as I can tell, the lineage for this 119 | particular tree is something like: 120 | 121 | * Keith Packard (original author) 122 | * Matthew Hawn 123 | * ... 124 | * Dana Jansens 125 | * Christopher Jeffrey 126 | * Tycho Kirchner 127 | 128 | Not counting the tens of people who forked it in between. 129 | 130 | See LICENSE for more info. 131 | -------------------------------------------------------------------------------- /cm-global.c: -------------------------------------------------------------------------------- 1 | 2 | #include "cm-global.h" 3 | 4 | 5 | Atom atom_opacity; 6 | Atom atom_win_type; 7 | Atom atom_pixmap; 8 | Atom atom_wm_state; 9 | Atom atom_net_frame_extents; 10 | Atom atom_gtk_frame_extents; 11 | 12 | Display* g_dpy = NULL; 13 | int g_screen = 0; 14 | -------------------------------------------------------------------------------- /cm-global.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern Atom atom_opacity; 6 | extern Atom atom_win_type; 7 | extern Atom atom_pixmap; 8 | extern Atom atom_wm_state; 9 | extern Atom atom_net_frame_extents; 10 | extern Atom atom_gtk_frame_extents; 11 | 12 | 13 | extern Display* g_dpy; 14 | extern int g_screen; 15 | -------------------------------------------------------------------------------- /cm-root.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "cm-root.h" 7 | #include "cm-global.h" 8 | 9 | Window root; 10 | Picture root_picture; 11 | Picture root_buffer; 12 | int root_width; 13 | int root_height; 14 | 15 | const char *root_background_props[] = { 16 | "_XROOTPMAP_ID", 17 | "_XSETROOT_ID", 18 | 0 19 | }; 20 | 21 | 22 | static inline int 23 | _get_valid_pixmap_depth(Pixmap pxmap) { 24 | if (!pxmap) return 0; 25 | 26 | Window rroot = None; 27 | int rx = 0, ry = 0; 28 | unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0; 29 | // In some window managers without managed desktops or also in some versions of 30 | // xfce (4.18), the found pixmap is invalid having a size of zero. 31 | bool is_valid = XGetGeometry(g_dpy, pxmap, &rroot, &rx, &ry, 32 | &rwid, &rhei, &rborder, &rdepth) && rwid && rhei; 33 | if(is_valid){ 34 | return rdepth; 35 | } 36 | return 0; 37 | } 38 | 39 | 40 | // XRenderFind(Standard)Format() is a roundtrip, so cache the results 41 | static XRenderPictFormat* renderformats[ 33 ] = {NULL}; 42 | 43 | static Picture _create_background_pict(Pixmap pix, int depth) 44 | { 45 | XRenderPictureAttributes pa; 46 | // Stay safe, and do not cache the fallback render format without further research. 47 | renderformats[0] = NULL; 48 | if (renderformats[depth] == NULL) { 49 | switch(depth){ 50 | case 0: 51 | break; 52 | case 1: 53 | renderformats[1] = XRenderFindStandardFormat(g_dpy, PictStandardA1); 54 | break; 55 | case 8: 56 | renderformats[8] = XRenderFindStandardFormat(g_dpy, PictStandardA8); 57 | break; 58 | case 24: 59 | renderformats[24] = XRenderFindStandardFormat(g_dpy, PictStandardRGB24); 60 | break; 61 | case 32: 62 | renderformats[32] = XRenderFindStandardFormat(g_dpy, PictStandardARGB32); 63 | break; 64 | default: { 65 | fprintf(stderr, "Unhandled root background depth %d - please report!\n", depth); 66 | break; 67 | } 68 | } 69 | if (renderformats[depth] == NULL) { 70 | // Use renderformats[0] for all fallback-depths 71 | depth = 0; 72 | renderformats[0] = XRenderFindVisualFormat(g_dpy, DefaultVisual(g_dpy, g_screen)); 73 | } 74 | } 75 | 76 | pa.repeat = True; 77 | return XRenderCreatePicture(g_dpy, pix, renderformats[depth], CPRepeat, &pa); 78 | } 79 | 80 | bool root_init(){ 81 | XRenderPictureAttributes pa; 82 | root_width = DisplayWidth(g_dpy, g_screen); 83 | root_height = DisplayHeight(g_dpy, g_screen); 84 | 85 | pa.subwindow_mode = IncludeInferiors; 86 | root_picture = XRenderCreatePicture(g_dpy, root, 87 | XRenderFindVisualFormat(g_dpy, DefaultVisual(g_dpy, g_screen)), 88 | CPSubwindowMode, &pa); 89 | return true; 90 | } 91 | 92 | /// Create the root background picture. First check, if the root window already 93 | /// has a valid corresponding pixmap. If so, do not overwrite it, such that e.g. 94 | /// openbox's root background image is preserved. Create the picture using the 95 | /// same depth, otherwise we're flooded with errors like 96 | /// "error 143 (BadPicture) request 139 minor 8 serial 78698". If no valid 97 | /// background pixmap is found, we create one ourselves using DefaultVisual() 98 | /// and set a fixed solid background color. 99 | Picture root_create_tile() { 100 | Picture picture; 101 | Atom actual_type; 102 | Pixmap pixmap; 103 | int actual_format; 104 | unsigned long nitems; 105 | unsigned long bytes_after; 106 | unsigned char *prop; 107 | unsigned pict_depth = 0; 108 | bool fill; 109 | int p; 110 | int res; 111 | const char* valid_pix_str; 112 | 113 | pixmap = None; 114 | 115 | for (p=0; root_background_props[p]; p++) { 116 | prop = NULL; 117 | res = XGetWindowProperty(g_dpy, root, 118 | XInternAtom(g_dpy, root_background_props[p], False), 119 | 0, 4, False, AnyPropertyType, &actual_type, 120 | &actual_format, &nitems, &bytes_after, &prop); 121 | if (res != Success || prop == NULL ){ 122 | continue; 123 | } 124 | if(actual_type == atom_pixmap 125 | && actual_format == 32 && nitems == 1) { 126 | memcpy(&pixmap, prop, 4); 127 | } 128 | XFree(prop); 129 | pict_depth = _get_valid_pixmap_depth(pixmap); 130 | if(pict_depth){ 131 | break; 132 | } else { 133 | pixmap = None; 134 | } 135 | } 136 | 137 | if(pixmap == None){ 138 | valid_pix_str = "invalid"; 139 | pixmap = XCreatePixmap(g_dpy, root, 1, 1, DefaultDepth(g_dpy, g_screen)); 140 | fill = true; 141 | } else { 142 | valid_pix_str = "valid"; 143 | fill = false; 144 | } 145 | fprintf(stderr, "info: root background pixmap is %s.\n", valid_pix_str); 146 | picture = _create_background_pict(pixmap, pict_depth); 147 | 148 | if (fill) { 149 | XRenderColor c; 150 | c.red = c.green = c.blue = 0x8080; 151 | c.alpha = 0xffff; 152 | XRenderFillRectangle( 153 | g_dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); 154 | } 155 | return picture; 156 | } 157 | 158 | 159 | -------------------------------------------------------------------------------- /cm-root.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | extern Window root; 9 | extern Picture root_picture; 10 | extern Picture root_buffer; 11 | extern int root_width; 12 | extern int root_height; 13 | extern const char *root_background_props[]; 14 | 15 | 16 | bool root_init(); 17 | Picture root_create_tile(); 18 | -------------------------------------------------------------------------------- /cm-util.c: -------------------------------------------------------------------------------- 1 | 2 | #include "cm-util.h" 3 | 4 | time_t _program_start_secs = 0; 5 | -------------------------------------------------------------------------------- /cm-util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | extern time_t _program_start_secs; 7 | 8 | #define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) 9 | 10 | #define READ_ONCE(x) \ 11 | ({ typeof(x) ___x = ACCESS_ONCE(x); ___x; }) 12 | 13 | #define WRITE_ONCE(x, val) \ 14 | do { ACCESS_ONCE(x) = (val); } while (0) 15 | 16 | static inline int 17 | get_time_in_milliseconds() { 18 | struct timeval tv; 19 | gettimeofday(&tv, NULL); 20 | return (tv.tv_sec-_program_start_secs) * 1000 + tv.tv_usec / 1000; 21 | } 22 | 23 | // normalize double to range 0-1 24 | static inline double normalize_d(double d) { 25 | if (d > 1.0) 26 | return 1.0; 27 | if (d < 0.0) 28 | return 0.0; 29 | 30 | return d; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /comp_rect.c: -------------------------------------------------------------------------------- 1 | 2 | #include "comp_rect.h" 3 | 4 | 5 | 6 | /// Returns true, if r1 fully contains r2 7 | static bool rect_contains(CompRect* r1, CompRect* r2){ 8 | return r1->x1 <= r2->x1 && r1->y1 <= r2->y1 && 9 | r1->x2 >= r2->x2 && r1->y2 >=r2->y2; 10 | } 11 | 12 | 13 | static bool rects_are_intersecting(CompRect* r1, CompRect* r2) 14 | { 15 | // if the left point of one rect is greater 16 | // than the right one of the other, nothing intersects. 17 | if(r1->x1 > r2->x2 || r2->x1 > r1->x2){ 18 | return false; 19 | } 20 | if(r1->y1 > r2->y2 || r2->y1 > r1->y2){ 21 | return false; 22 | } 23 | return true; 24 | } 25 | 26 | /// Check if we can omit painting a window (rect). E.g., a window 27 | /// completely occluded by another one, does not need to be 28 | /// painted. Further, we try to select the largest possible ignore region 29 | /// Based on window and intersection areas. 30 | bool rect_paint_needed(CompRect* ignore_reg, CompRect* reg){ 31 | if(rect_contains(ignore_reg, reg)){ 32 | // the ignore-region completely occludes the window. 33 | return false; 34 | } 35 | if(! rects_are_intersecting(ignore_reg, reg)){ 36 | // KISS and just use the greater rect as new ignore region. 37 | if(reg->w*reg->h > ignore_reg->w*ignore_reg->h){ 38 | *ignore_reg = *reg; 39 | } 40 | return true; 41 | } 42 | 43 | // calculate the intersection rect. 44 | short x1 = (ignore_reg->x1 > reg->x1) ? ignore_reg->x1 : reg->x1; 45 | short x2 = (ignore_reg->x2 < reg->x2) ? ignore_reg->x1 : reg->x1; 46 | short y1 = (ignore_reg->y1 > reg->y1) ? ignore_reg->y1 : reg->y1; 47 | short y2 = (ignore_reg->y2 < reg->y2) ? ignore_reg->y1 : reg->y1; 48 | short w = x2 - x1; 49 | short h = y2 - y1; 50 | 51 | // KISS and just use the biggest rect as new ignore rect 52 | if(reg->w*reg->h > ignore_reg->w*ignore_reg->h){ 53 | *ignore_reg = *reg; 54 | } 55 | if(w*h > ignore_reg->w*ignore_reg->h){ 56 | CompRect r_intersect = {.x1 = x1, .y1 = y1, 57 | .x2 = x2, .y2 = y2, 58 | .w = w, .h = h }; 59 | *ignore_reg = r_intersect; 60 | } 61 | return true; 62 | } 63 | -------------------------------------------------------------------------------- /comp_rect.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct { 6 | short x1; 7 | short y1; 8 | short x2; 9 | short y2; 10 | short w; 11 | short h; 12 | } CompRect; 13 | 14 | 15 | 16 | bool rect_paint_needed(CompRect* ignore_reg, CompRect* reg); 17 | -------------------------------------------------------------------------------- /fastcompmgr.1: -------------------------------------------------------------------------------- 1 | .ds q \N'34' 2 | .TH fastcompmgr 1 3 | .SH NAME 4 | fastcompmgr \- a _fast_ compositor for X11 5 | .SH SYNOPSIS 6 | .nf 7 | .B fastcompmgr [\-d display] [\-r radius] [\-o opacity] [\-l left-offset] [\-t top-offset] [\-i opacity] [\-e opacity] [\-cCfFS] 8 | .fi 9 | .SH DESCRIPTION 10 | .B fastcompmgr 11 | is a compositor based on Dana Jansens' version of xcompmgr (which itself was 12 | written by Keith Packard) and on an early verion of Christopher Jeffrey's 13 | compton. It includes many improvements over the original 14 | xcompmgr, including window frame opacity, inactive window transparency, 15 | and shadows on argb windows. 16 | .SH OPTIONS 17 | .TP 18 | .BI \-d\ display 19 | Specifies the display to manage. 20 | .TP 21 | .BI \-r\ radius 22 | Specifies the blur radius for client-side shadows. 23 | .TP 24 | .BI \-o\ opacity 25 | Specifies the opacity for client-side shadows. 26 | .TP 27 | .BI \-l\ left-offset 28 | Specifies the left offset for client-side shadows. 29 | .TP 30 | .BI \-t\ top-offset 31 | Specifies the top offset for client-side shadows. 32 | .TP 33 | .BI \-I\ fade-in-step 34 | Specifies the opacity change between steps while fading in. 35 | .TP 36 | .BI \-O\ fade-out-step 37 | Specifies the opacity change between steps while fading out. 38 | .TP 39 | .BI \-D\ fade-delta 40 | Specifies the time (in milliseconds) between steps in a fade. 41 | .TP 42 | .BI \-c 43 | Enable client-side shadows on windows. 44 | .TP 45 | .BI \-f 46 | When \-c is specified, enables a smooth fade effect for transient windows like 47 | menus, and for all windows on hide and restore events. 48 | .TP 49 | .BI \-C 50 | When \-c is specified, attempts to avoid painting shadows on panels and docks. 51 | .TP 52 | .BI \-F 53 | When \-f is specified, also enables the fade effect when windows change their 54 | opacity, as with transset(1). 55 | .TP 56 | .BI \-i\ opacity 57 | Specifies inactive window transparency. (0.1 - 1.0) 58 | .TP 59 | .BI \-e\ opacity 60 | Specifies window frame transparency. (0.1 - 1.0) 61 | .TP 62 | .BI \-S 63 | Enables synchronous operation. Useful for debugging. 64 | .SH BUGS 65 | Bugs may be reported to https://github.com/tycho-kirchner/fastcompmgr 66 | .SH AUTHORS 67 | xcompmgr, originally written by Keith Packard, with contributions from 68 | Matthew Allum, Eric Anholt, Dan Doel, Thomas Luebking, Matthew Hawn, 69 | Ely Levy, Phil Blundell, and Carl Worth. 70 | Compton by Christopher Jeffrey, based on Dana Jansens' original work. 71 | fastcompmgr by Tycho Kirchner. 72 | -------------------------------------------------------------------------------- /fastcompmgr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * fastcompmgr - a _fast_ compositor for X11 3 | * 4 | * based on xcompmgr - copyright (c) 2003, keith packard 5 | * 2008, Dana Jansens 6 | * compton 2011, Christopher Jeffrey 7 | * fastcompmgr 2023, Tycho Kirchner 8 | * 9 | * copyright (c) 2023, Tycho Kirchner 10 | * See LICENSE for more information. 11 | * 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "cm-global.h" 33 | #include "cm-root.h" 34 | #include "cm-util.h" 35 | #include "comp_rect.h" 36 | #include "ringbuffer.h" 37 | 38 | #define likely(x) __builtin_expect(!!(x), 1) 39 | #define unlikely(x) __builtin_expect(!!(x), 0) 40 | 41 | 42 | #if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2 43 | #define HAS_NAME_WINDOW_PIXMAP 1 44 | #endif 45 | 46 | #define CAN_DO_USABLE 0 47 | 48 | typedef enum { 49 | WINTYPE_UNKNOWN, // MUST ALWAYS STAY first, due to init optimization in add_win 50 | WINTYPE_DESKTOP, 51 | WINTYPE_DOCK, 52 | WINTYPE_TOOLBAR, 53 | WINTYPE_MENU, 54 | WINTYPE_UTILITY, 55 | WINTYPE_SPLASH, 56 | WINTYPE_DIALOG, 57 | WINTYPE_NORMAL, 58 | WINTYPE_DROPDOWN_MENU, 59 | WINTYPE_POPUP_MENU, 60 | WINTYPE_TOOLTIP, 61 | WINTYPE_NOTIFY, 62 | WINTYPE_COMBO, 63 | WINTYPE_DND, 64 | NUM_WINTYPES 65 | } wintype; 66 | 67 | 68 | // Cache whether to draw a shadow or not 69 | typedef enum { 70 | SHADOW_UNKNWON, // MUST ALWAYS STAY first, due to init optimization in add_win 71 | SHADOW_YES, 72 | SHADOW_NO 73 | } shadowtype; 74 | 75 | typedef struct _ignore { 76 | struct _ignore *next; 77 | unsigned long sequence; 78 | } ignore; 79 | 80 | typedef struct _win { 81 | struct _win *next; 82 | Window id; 83 | #if HAS_NAME_WINDOW_PIXMAP 84 | Pixmap pixmap; 85 | #endif 86 | XWindowAttributes a; 87 | #if CAN_DO_USABLE 88 | Bool usable; /* mapped and all damaged at one point */ 89 | XRectangle damage_bounds; /* bounds of damage */ 90 | #endif 91 | int mode; 92 | int damaged; 93 | Damage damage; 94 | Picture picture; 95 | Picture alpha_pict; 96 | Picture alpha_border_pict; 97 | Picture shadow_pict; 98 | XserverRegion border_size; 99 | XserverRegion extents; 100 | Picture shadow; 101 | int shadow_dx; 102 | int shadow_dy; 103 | int shadow_width; 104 | int shadow_height; 105 | unsigned int opacity; 106 | wintype window_type; 107 | shadowtype shadow_type; 108 | unsigned long damage_sequence; /* sequence when damage was created */ 109 | Bool destroyed; 110 | Bool paint_needed; 111 | unsigned int left_width; 112 | unsigned int right_width; 113 | unsigned int top_width; 114 | unsigned int bottom_width; 115 | 116 | Bool need_configure; 117 | bool configure_size_changed; 118 | XConfigureEvent queue_configure; 119 | 120 | /* for drawing translucent windows */ 121 | XserverRegion border_clip; 122 | struct _win *prev_trans; 123 | } win; 124 | 125 | typedef struct _conv { 126 | int size; 127 | double *data; 128 | } conv; 129 | 130 | typedef struct _fade { 131 | struct _fade *next; 132 | win *w; 133 | double cur; 134 | double finish; 135 | double step; 136 | void (*callback) (Display *dpy, win *w); 137 | Display *dpy; 138 | } fade; 139 | 140 | win *list; 141 | fade *fades; 142 | Display *dpy; 143 | Picture black_picture; 144 | Picture cshadow_picture; 145 | Picture root_tile; 146 | XserverRegion all_damage; 147 | XserverRegion g_xregion_tmp; 148 | Bool all_damage_is_dirty; 149 | Bool clip_changed; 150 | #if HAS_NAME_WINDOW_PIXMAP 151 | Bool has_name_pixmap; 152 | #endif 153 | ringBuffer_typedef(ulong, IgnoreErrRingbuf); 154 | IgnoreErrRingbuf ignore_ringbuf; 155 | IgnoreErrRingbuf* p_ignore_ringbuf = &ignore_ringbuf; 156 | int xfixes_event, xfixes_error; 157 | int damage_event, damage_error; 158 | int composite_event, composite_error; 159 | int render_event, render_error; 160 | Bool synchronize; 161 | int composite_opcode; 162 | static Bool g_paint_ignore_region_is_dirty = True; 163 | 164 | Atom win_type[NUM_WINTYPES]; 165 | double win_type_opacity[NUM_WINTYPES]; 166 | Bool win_type_shadow[NUM_WINTYPES]; 167 | Bool win_type_fade[NUM_WINTYPES]; 168 | 169 | #define REGISTER_PROP "_NET_WM_CM_S" 170 | 171 | #define OPAQUE 0xffffffff 172 | 173 | conv *gaussian_map; 174 | 175 | #define WINDOW_SOLID 0 176 | #define WINDOW_TRANS 1 177 | #define WINDOW_ARGB 2 178 | 179 | #ifndef DEBUG_REPAINT 180 | #define DEBUG_REPAINT 0 181 | #endif 182 | #ifndef DEBUG_EVENTS 183 | #define DEBUG_EVENTS 0 184 | #endif 185 | #ifndef MONITOR_REPAINT 186 | #define MONITOR_REPAINT 0 187 | #endif 188 | 189 | static void 190 | determine_mode(Display *dpy, win *w); 191 | static bool 192 | is_gtk_frame_extent(Display *dpy, Window w); 193 | 194 | static double 195 | get_opacity_percent(Display *dpy, win *w); 196 | static void 197 | do_configure_win(Display *dpy, win* w); 198 | static void 199 | set_paint_ignore_region_dirty(void); 200 | 201 | static XserverRegion 202 | win_extents(Display *dpy, win *w); 203 | 204 | int shadow_radius = 12; 205 | int shadow_offset_x = -15; 206 | int shadow_offset_y = -15; 207 | double shadow_opacity = .75; 208 | 209 | double fade_in_step = 0.028; 210 | double fade_out_step = 0.03; 211 | int fade_delta = 10; 212 | int fade_time = 0; 213 | Bool fade_trans = False; 214 | 215 | double inactive_opacity = 0; 216 | double frame_opacity = 0; 217 | 218 | #define INACTIVE_OPACITY \ 219 | (unsigned long)((double)inactive_opacity * OPAQUE) 220 | 221 | #define IS_NORMAL_WIN(w) \ 222 | ((w) && ((w)->window_type == WINTYPE_NORMAL \ 223 | || (w)->window_type == WINTYPE_UTILITY)) 224 | 225 | #define HAS_FRAME_OPACITY(w) (frame_opacity && (w)->top_width) 226 | 227 | /* For shadow precomputation */ 228 | int Gsize = -1; 229 | unsigned char *shadow_corner = NULL; 230 | unsigned char *shadow_top = NULL; 231 | 232 | 233 | fade * 234 | find_fade(win *w) { 235 | fade *f; 236 | 237 | for (f = fades; f; f = f->next) { 238 | if (f->w == w) return f; 239 | } 240 | 241 | return 0; 242 | } 243 | 244 | void 245 | dequeue_fade(Display *dpy, fade *f) { 246 | fade **prev; 247 | 248 | for (prev = &fades; *prev; prev = &(*prev)->next) { 249 | if (*prev == f) { 250 | *prev = f->next; 251 | if (f->callback) { 252 | (*f->callback)(dpy, f->w); 253 | } 254 | free(f); 255 | break; 256 | } 257 | } 258 | } 259 | 260 | void 261 | cleanup_fade(Display *dpy, win *w) { 262 | fade *f = find_fade (w); 263 | if (f) { 264 | dequeue_fade(dpy, f); 265 | } 266 | } 267 | 268 | void 269 | enqueue_fade(Display *dpy, fade *f) { 270 | if (!fades) { 271 | fade_time = get_time_in_milliseconds() + fade_delta; 272 | } 273 | f->next = fades; 274 | fades = f; 275 | } 276 | 277 | static void 278 | set_fade(Display *dpy, win *w, double start, 279 | double finish, double step, 280 | void(*callback) (Display *dpy, win *w), 281 | Bool exec_callback, Bool override) { 282 | fade *f; 283 | 284 | f = find_fade(w); 285 | if (!f) { 286 | f = malloc(sizeof(fade)); 287 | f->next = 0; 288 | f->w = w; 289 | f->cur = start; 290 | enqueue_fade(dpy, f); 291 | } else if (!override) { 292 | return; 293 | } else { 294 | if (exec_callback && f->callback) { 295 | (*f->callback)(dpy, f->w); 296 | } 297 | } 298 | 299 | if (finish < 0) finish = 0; 300 | if (finish > 1) finish = 1; 301 | f->finish = finish; 302 | 303 | if (f->cur < finish) { 304 | f->step = step; 305 | } else if (f->cur > finish) { 306 | f->step = -step; 307 | } 308 | 309 | f->callback = callback; 310 | w->opacity = f->cur * OPAQUE; 311 | 312 | #if 0 313 | printf("set_fade start %g step %g\n", f->cur, f->step); 314 | #endif 315 | 316 | determine_mode(dpy, w); 317 | 318 | if (w->shadow) { 319 | // rebuild the shadow 320 | XRenderFreePicture(dpy, w->shadow); 321 | w->shadow = None; 322 | win_extents(dpy, w); 323 | } 324 | 325 | /* fading windows need to be drawn, mark 326 | them as damaged. when a window maps, 327 | if it tries to fade in but it already 328 | at the right opacity (map/unmap/map fast) 329 | then it will never get drawn without this 330 | until it repaints */ 331 | w->damaged = 1; 332 | } 333 | 334 | int 335 | fade_timeout(void) { 336 | int now; 337 | int delta; 338 | 339 | if (!fades) return -1; 340 | 341 | now = get_time_in_milliseconds(); 342 | delta = fade_time - now; 343 | 344 | if (delta < 0) delta = 0; 345 | /* printf("timeout %d\n", delta); */ 346 | 347 | return delta; 348 | } 349 | 350 | void 351 | run_fades(Display *dpy) { 352 | int now = get_time_in_milliseconds(); 353 | fade *next = fades; 354 | int steps; 355 | Bool need_dequeue; 356 | 357 | #if 0 358 | printf("run fades\n"); 359 | #endif 360 | 361 | if (fade_time - now > 0) return; 362 | steps = 1 + (now - fade_time) / fade_delta; 363 | 364 | while (next) { 365 | fade *f = next; 366 | win *w = f->w; 367 | next = f->next; 368 | 369 | f->cur += f->step * steps; 370 | if (f->cur >= 1) { 371 | f->cur = 1; 372 | } else if (f->cur < 0) { 373 | f->cur = 0; 374 | } 375 | 376 | #if 0 377 | printf("opacity now %g\n", f->cur); 378 | #endif 379 | 380 | w->opacity = f->cur * OPAQUE; 381 | need_dequeue = False; 382 | if (f->step > 0) { 383 | if (f->cur >= f->finish) { 384 | w->opacity = f->finish * OPAQUE; 385 | need_dequeue = True; 386 | } 387 | } else { 388 | if (f->cur <= f->finish) { 389 | w->opacity = f->finish * OPAQUE; 390 | need_dequeue = True; 391 | } 392 | } 393 | 394 | determine_mode(dpy, w); 395 | 396 | if (w->shadow) { 397 | // rebuild the shadow 398 | XRenderFreePicture(dpy, w->shadow); 399 | w->shadow = None; 400 | win_extents(dpy, w); 401 | } 402 | 403 | /* Must do this last as it might 404 | destroy f->w in callbacks */ 405 | if (need_dequeue) dequeue_fade(dpy, f); 406 | } 407 | 408 | fade_time = now + fade_delta; 409 | } 410 | 411 | static double 412 | gaussian(double r, double x, double y) { 413 | return ((1 / (sqrt(2 * M_PI * r))) * 414 | exp((- (x * x + y * y)) / (2 * r * r))); 415 | } 416 | 417 | 418 | static conv * 419 | make_gaussian_map(Display *dpy, double r) { 420 | conv *c; 421 | int size = ((int) ceil((r * 3)) + 1) & ~1; 422 | int center = size / 2; 423 | int x, y; 424 | double t; 425 | double g; 426 | 427 | c = malloc(sizeof(conv) + size * size * sizeof(double)); 428 | c->size = size; 429 | c->data = (double *) (c + 1); 430 | t = 0.0; 431 | 432 | for (y = 0; y < size; y++) { 433 | for (x = 0; x < size; x++) { 434 | g = gaussian(r, (double) (x - center), (double) (y - center)); 435 | t += g; 436 | c->data[y * size + x] = g; 437 | } 438 | } 439 | 440 | /* printf("gaussian total %f\n", t); */ 441 | 442 | for (y = 0; y < size; y++) { 443 | for (x = 0; x < size; x++) { 444 | c->data[y * size + x] /= t; 445 | } 446 | } 447 | 448 | return c; 449 | } 450 | 451 | /* 452 | * A picture will help 453 | * 454 | * -center 0 width width+center 455 | * -center +-----+-------------------+-----+ 456 | * | | | | 457 | * | | | | 458 | * 0 +-----+-------------------+-----+ 459 | * | | | | 460 | * | | | | 461 | * | | | | 462 | * height +-----+-------------------+-----+ 463 | * | | | | 464 | * height+ | | | | 465 | * center +-----+-------------------+-----+ 466 | */ 467 | 468 | static unsigned char 469 | sum_gaussian(conv *map, double opacity, 470 | int x, int y, int width, int height) { 471 | int fx, fy; 472 | double *g_data; 473 | double *g_line = map->data; 474 | int g_size = map->size; 475 | int center = g_size / 2; 476 | int fx_start, fx_end; 477 | int fy_start, fy_end; 478 | double v; 479 | 480 | /* 481 | * Compute set of filter values which are "in range", 482 | * that's the set with: 483 | * 0 <= x + (fx-center) && x + (fx-center) < width && 484 | * 0 <= y + (fy-center) && y + (fy-center) < height 485 | * 486 | * 0 <= x + (fx - center) x + fx - center < width 487 | * center - x <= fx fx < width + center - x 488 | */ 489 | 490 | fx_start = center - x; 491 | if (fx_start < 0) fx_start = 0; 492 | fx_end = width + center - x; 493 | if (fx_end > g_size) fx_end = g_size; 494 | 495 | fy_start = center - y; 496 | if (fy_start < 0) fy_start = 0; 497 | fy_end = height + center - y; 498 | if (fy_end > g_size) fy_end = g_size; 499 | 500 | g_line = g_line + fy_start * g_size + fx_start; 501 | 502 | v = 0; 503 | 504 | for (fy = fy_start; fy < fy_end; fy++) { 505 | g_data = g_line; 506 | g_line += g_size; 507 | 508 | for (fx = fx_start; fx < fx_end; fx++) { 509 | v += *g_data++; 510 | } 511 | } 512 | 513 | if (v > 1) v = 1; 514 | 515 | return ((unsigned char) (v * opacity * 255.0)); 516 | } 517 | 518 | /* precompute shadow corners and sides 519 | to save time for large windows */ 520 | static void 521 | presum_gaussian(conv *map) { 522 | int center = map->size/2; 523 | int opacity, x, y; 524 | 525 | Gsize = map->size; 526 | 527 | if (shadow_corner) free((void *)shadow_corner); 528 | if (shadow_top) free((void *)shadow_top); 529 | 530 | shadow_corner = (unsigned char *)(malloc((Gsize + 1) * (Gsize + 1) * 26)); 531 | shadow_top = (unsigned char *)(malloc((Gsize + 1) * 26)); 532 | 533 | for (x = 0; x <= Gsize; x++) { 534 | shadow_top[25 * (Gsize + 1) + x] = 535 | sum_gaussian(map, 1, x - center, center, Gsize * 2, Gsize * 2); 536 | 537 | for (opacity = 0; opacity < 25; opacity++) { 538 | shadow_top[opacity * (Gsize + 1) + x] = 539 | shadow_top[25 * (Gsize + 1) + x] * opacity / 25; 540 | } 541 | 542 | for (y = 0; y <= x; y++) { 543 | shadow_corner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] 544 | = sum_gaussian(map, 1, x - center, y - center, Gsize * 2, Gsize * 2); 545 | shadow_corner[25 * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y] 546 | = shadow_corner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]; 547 | 548 | for (opacity = 0; opacity < 25; opacity++) { 549 | shadow_corner[opacity * (Gsize + 1) * (Gsize + 1) 550 | + y * (Gsize + 1) + x] 551 | = shadow_corner[opacity * (Gsize + 1) * (Gsize + 1) 552 | + x * (Gsize + 1) + y] 553 | = shadow_corner[25 * (Gsize + 1) * (Gsize + 1) 554 | + y * (Gsize + 1) + x] * opacity / 25; 555 | } 556 | } 557 | } 558 | } 559 | 560 | static XImage * 561 | make_shadow(Display *dpy, double opacity, 562 | int width, int height) { 563 | XImage *ximage; 564 | unsigned char *data; 565 | int gsize = gaussian_map->size; 566 | int ylimit, xlimit; 567 | int swidth = width + gsize; 568 | int sheight = height + gsize; 569 | int center = gsize / 2; 570 | int x, y; 571 | unsigned char d; 572 | int x_diff; 573 | int opacity_int = (int)(opacity * 25); 574 | 575 | data = malloc(swidth * sheight * sizeof(unsigned char)); 576 | if (!data) return 0; 577 | 578 | ximage = XCreateImage( 579 | dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 8, 580 | ZPixmap, 0, (char *) data, swidth, sheight, 8, 581 | swidth * sizeof(unsigned char)); 582 | 583 | if (!ximage) { 584 | free(data); 585 | return 0; 586 | } 587 | 588 | /* 589 | * Build the gaussian in sections 590 | */ 591 | 592 | /* 593 | * center (fill the complete data array) 594 | */ 595 | 596 | if (Gsize > 0) { 597 | d = shadow_top[opacity_int * (Gsize + 1) + Gsize]; 598 | } else { 599 | d = sum_gaussian(gaussian_map, 600 | opacity, center, center, width, height); 601 | } 602 | 603 | memset(data, d, sheight * swidth); 604 | 605 | /* 606 | * corners 607 | */ 608 | 609 | ylimit = gsize; 610 | if (ylimit > sheight / 2) ylimit = (sheight + 1) / 2; 611 | 612 | xlimit = gsize; 613 | if (xlimit > swidth / 2) xlimit = (swidth + 1) / 2; 614 | 615 | for (y = 0; y < ylimit; y++) 616 | for (x = 0; x < xlimit; x++) { 617 | if (xlimit == Gsize && ylimit == Gsize) { 618 | d = shadow_corner[opacity_int * (Gsize + 1) * (Gsize + 1) 619 | + y * (Gsize + 1) + x]; 620 | } else { 621 | d = sum_gaussian(gaussian_map, 622 | opacity, x - center, y - center, width, height); 623 | } 624 | data[y * swidth + x] = d; 625 | data[(sheight - y - 1) * swidth + x] = d; 626 | data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d; 627 | data[y * swidth + (swidth - x - 1)] = d; 628 | } 629 | 630 | /* 631 | * top/bottom 632 | */ 633 | 634 | x_diff = swidth - (gsize * 2); 635 | if (x_diff > 0 && ylimit > 0) { 636 | for (y = 0; y < ylimit; y++) { 637 | if (ylimit == Gsize) { 638 | d = shadow_top[opacity_int * (Gsize + 1) + y]; 639 | } else { 640 | d = sum_gaussian(gaussian_map, 641 | opacity, center, y - center, width, height); 642 | } 643 | memset(&data[y * swidth + gsize], d, x_diff); 644 | memset(&data[(sheight - y - 1) * swidth + gsize], d, x_diff); 645 | } 646 | } 647 | 648 | /* 649 | * sides 650 | */ 651 | 652 | for (x = 0; x < xlimit; x++) { 653 | if (xlimit == Gsize) { 654 | d = shadow_top[opacity_int * (Gsize + 1) + x]; 655 | } else { 656 | d = sum_gaussian(gaussian_map, 657 | opacity, x - center, center, width, height); 658 | } 659 | for (y = gsize; y < sheight - gsize; y++) { 660 | data[y * swidth + x] = d; 661 | data[y * swidth + (swidth - x - 1)] = d; 662 | } 663 | } 664 | 665 | return ximage; 666 | } 667 | 668 | static Picture 669 | shadow_picture(Display *dpy, double opacity, Picture alpha_pict, 670 | int width, int height, int *wp, int *hp) { 671 | XImage *shadowImage; 672 | Pixmap shadowPixmap; 673 | Picture shadow_picture; 674 | GC gc; 675 | 676 | shadowImage = make_shadow(dpy, opacity, width, height); 677 | if (!shadowImage) return None; 678 | 679 | shadowPixmap = XCreatePixmap(dpy, root, 680 | shadowImage->width, shadowImage->height, 8); 681 | 682 | if (!shadowPixmap) { 683 | XDestroyImage(shadowImage); 684 | return None; 685 | } 686 | 687 | shadow_picture = XRenderCreatePicture(dpy, shadowPixmap, 688 | XRenderFindStandardFormat(dpy, PictStandardA8), 0, 0); 689 | if (!shadow_picture) { 690 | XDestroyImage(shadowImage); 691 | XFreePixmap(dpy, shadowPixmap); 692 | return None; 693 | } 694 | 695 | gc = XCreateGC(dpy, shadowPixmap, 0, 0); 696 | if (!gc) { 697 | XDestroyImage(shadowImage); 698 | XFreePixmap(dpy, shadowPixmap); 699 | XRenderFreePicture(dpy, shadow_picture); 700 | return None; 701 | } 702 | 703 | XPutImage( 704 | dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, 705 | shadowImage->width, shadowImage->height); 706 | 707 | *wp = shadowImage->width; 708 | *hp = shadowImage->height; 709 | XFreeGC(dpy, gc); 710 | XDestroyImage(shadowImage); 711 | XFreePixmap(dpy, shadowPixmap); 712 | 713 | return shadow_picture; 714 | } 715 | 716 | Picture 717 | solid_picture(Display *dpy, Bool argb, double a, 718 | double r, double g, double b) { 719 | Pixmap pixmap; 720 | Picture picture; 721 | XRenderPictureAttributes pa; 722 | XRenderColor c; 723 | 724 | pixmap = XCreatePixmap(dpy, root, 1, 1, argb ? 32 : 8); 725 | 726 | if (!pixmap) return None; 727 | 728 | pa.repeat = True; 729 | picture = XRenderCreatePicture(dpy, pixmap, 730 | XRenderFindStandardFormat(dpy, argb 731 | ? PictStandardARGB32 : PictStandardA8), 732 | CPRepeat, 733 | &pa); 734 | 735 | if (!picture) { 736 | XFreePixmap(dpy, pixmap); 737 | return None; 738 | } 739 | 740 | c.alpha = a * 0xffff; 741 | c.red = r * 0xffff; 742 | c.green = g * 0xffff; 743 | c.blue = b * 0xffff; 744 | 745 | XRenderFillRectangle(dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); 746 | XFreePixmap(dpy, pixmap); 747 | 748 | return picture; 749 | } 750 | 751 | void 752 | discard_ignore(Display *dpy, unsigned long sequence) { 753 | while(! isBufferEmpty(p_ignore_ringbuf)){ 754 | ulong buf_seq; 755 | buf_seq = bufferReadPeek(p_ignore_ringbuf); 756 | if ((long) (sequence - buf_seq) > 0) { 757 | bufferReadSkip(p_ignore_ringbuf); 758 | } else { 759 | break; 760 | } 761 | } 762 | } 763 | 764 | void 765 | set_ignore(Display *dpy, unsigned long sequence) { 766 | if(unlikely(isBufferFull(p_ignore_ringbuf))) { 767 | bufferIncrease(p_ignore_ringbuf, p_ignore_ringbuf->size*2); 768 | } 769 | bufferWrite(p_ignore_ringbuf, sequence); 770 | } 771 | 772 | int 773 | should_ignore(Display *dpy, unsigned long sequence) { 774 | ulong buf_seq; 775 | discard_ignore(dpy, sequence); 776 | if(isBufferEmpty(p_ignore_ringbuf)) return False; 777 | buf_seq = bufferReadPeek(p_ignore_ringbuf); 778 | return buf_seq == sequence; 779 | } 780 | 781 | static win * 782 | find_win(Display *dpy, Window id) { 783 | win *w; 784 | 785 | for (w = list; w; w = w->next) { 786 | if (w->id == id && !w->destroyed) 787 | return w; 788 | } 789 | 790 | return 0; 791 | } 792 | 793 | static void 794 | paint_root(Display *dpy) { 795 | if (!root_tile) { 796 | root_tile = root_create_tile(); 797 | } 798 | 799 | XRenderComposite( 800 | dpy, PictOpSrc, root_tile, None, 801 | root_buffer, 0, 0, 0, 0, 0, 0, 802 | root_width, root_height); 803 | } 804 | 805 | static XserverRegion 806 | win_extents(Display *dpy, win *w) { 807 | XRectangle r; 808 | 809 | r.x = w->a.x; 810 | r.y = w->a.y; 811 | r.width = w->a.width + w->a.border_width * 2; 812 | r.height = w->a.height + w->a.border_width * 2; 813 | 814 | if(unlikely(w->shadow_type == SHADOW_UNKNWON)){ 815 | // override_redirect: looking at xlib's documentation for the "Override Redirect Flag", it becomes 816 | // clear that toolkits will typically set this flag for popup windows. 817 | // On the other hand, WINTYPE_NORMAL windows setting override_redirect, are likely 818 | // some kind of special windows, as seen in zoom screenshare. At least in zoom's case, 819 | // the shadow darkens the whole desktop. A better fix might be to render 820 | // four shadow images around the window instead of one huge shadow. But first 821 | // check, why dcompmgr does not have this problem. 822 | // See also: https://github.com/regolith-linux/regolith-compositor-compton-glx/issues/3 823 | bool shadow_yes = (likely(w->window_type) 824 | && win_type_shadow[w->window_type] && 825 | (! w->a.override_redirect || w->window_type != WINTYPE_NORMAL) && 826 | ! is_gtk_frame_extent(dpy, w->id)); 827 | // Firefox's bookmark-dragging renders a large ugly shadow. Since these as 828 | // well as Tab-popups are ARGB-windows, stay safe and only draw shadows on 829 | // non-solid windows for types normal/dialog . Note that apparently Compiz 830 | // does not draw shadows on any ARGB windows, so nothing unusual here. See 831 | // also https://github.com/chjj/compton/issues/201 832 | // Since we have the -C flag to control for shadows on panels/docks, respect 833 | // this setting by below WINTYPE_DOCK (and above win_type_shadow[]) 834 | if (w->mode != WINDOW_SOLID ) { 835 | switch(w->window_type){ 836 | case WINTYPE_NORMAL: 837 | case WINTYPE_DIALOG: 838 | case WINTYPE_DOCK: 839 | shadow_yes = shadow_yes && true; break; 840 | default: 841 | shadow_yes = false; 842 | } 843 | } 844 | w->shadow_type = (shadow_yes) ? SHADOW_YES : SHADOW_NO; 845 | } 846 | 847 | if (w->shadow_type == SHADOW_YES) { 848 | XRectangle sr; 849 | 850 | w->shadow_dx = shadow_offset_x; 851 | w->shadow_dy = shadow_offset_y; 852 | 853 | if (!w->shadow) { 854 | double opacity = shadow_opacity; 855 | 856 | if (w->mode != WINDOW_SOLID) { 857 | opacity = opacity * ((double)w->opacity) / ((double)OPAQUE); 858 | } 859 | 860 | if (HAS_FRAME_OPACITY(w)) { 861 | opacity = opacity * frame_opacity; 862 | } 863 | 864 | w->shadow = shadow_picture( 865 | dpy, opacity, w->alpha_pict, 866 | w->a.width + w->a.border_width * 2, 867 | w->a.height + w->a.border_width * 2, 868 | &w->shadow_width, &w->shadow_height); 869 | } 870 | 871 | sr.x = w->a.x + w->shadow_dx; 872 | sr.y = w->a.y + w->shadow_dy; 873 | sr.width = w->shadow_width; 874 | sr.height = w->shadow_height; 875 | 876 | if (sr.x < r.x) { 877 | r.width = (r.x + r.width) - sr.x; 878 | r.x = sr.x; 879 | } 880 | 881 | if (sr.y < r.y) { 882 | r.height = (r.y + r.height) - sr.y; 883 | r.y = sr.y; 884 | } 885 | 886 | if (sr.x + sr.width > r.x + r.width) { 887 | r.width = sr.x + sr.width - r.x; 888 | } 889 | 890 | if (sr.y + sr.height > r.y + r.height) { 891 | r.height = sr.y + sr.height - r.y; 892 | } 893 | } 894 | if(! w->extents){ 895 | w->extents = XFixesCreateRegion(dpy, &r, 1); 896 | } else { 897 | XFixesSetRegion(dpy, w->extents, &r, 1); 898 | } 899 | return w->extents; 900 | 901 | } 902 | 903 | static XserverRegion 904 | border_size(Display *dpy, win *w) { 905 | XserverRegion border; 906 | 907 | /* 908 | * if window doesn't exist anymore, this will generate an error 909 | * as well as not generate a region. Perhaps a better XFixes 910 | * architecture would be to have a request that copies instead 911 | * of creates, that way you'd just end up with an empty region 912 | * instead of an invalid XID. 913 | */ 914 | 915 | set_ignore(dpy, NextRequest(dpy)); 916 | border = XFixesCreateRegionFromWindow( 917 | dpy, w->id, WindowRegionBounding); 918 | 919 | /* translate this */ 920 | set_ignore(dpy, NextRequest(dpy)); 921 | XFixesTranslateRegion(dpy, border, 922 | w->a.x + w->a.border_width, 923 | w->a.y + w->a.border_width); 924 | 925 | return border; 926 | } 927 | 928 | static Window 929 | find_client_win(Display *dpy, Window win) { 930 | Window root, parent; 931 | Window *children; 932 | unsigned int nchildren; 933 | unsigned int i; 934 | Atom type = None; 935 | int format; 936 | unsigned long nitems, after; 937 | unsigned char *data = NULL; 938 | Window client = 0; 939 | int res; 940 | 941 | res = XGetWindowProperty( 942 | dpy, win, atom_wm_state, 0, 0, False, 943 | AnyPropertyType, &type, &format, &nitems, 944 | &after, &data); 945 | if (likely(res == Success && data != NULL )) { 946 | XFree(data); 947 | if (likely(type)) return win; 948 | } 949 | 950 | if (!XQueryTree(dpy, win, &root, 951 | &parent, &children, &nchildren)) { 952 | return 0; 953 | } 954 | 955 | for (i = 0; i < nchildren; i++) { 956 | client = find_client_win(dpy, children[i]); 957 | if (client) break; 958 | } 959 | 960 | if (children) XFree((char *)children); 961 | 962 | return client; 963 | } 964 | 965 | static void 966 | get_frame_extents(Display *dpy, Window w, 967 | unsigned int *left, 968 | unsigned int *right, 969 | unsigned int *top, 970 | unsigned int *bottom) { 971 | long *extents; 972 | Atom type; 973 | int format; 974 | unsigned long nitems, after; 975 | unsigned char *data = NULL; 976 | int result; 977 | 978 | *left = 0; 979 | *right = 0; 980 | *top = 0; 981 | *bottom = 0; 982 | 983 | w = find_client_win(dpy, w); 984 | if (!w) return; 985 | 986 | result = XGetWindowProperty( 987 | dpy, w, atom_net_frame_extents, 988 | 0L, 4L, False, AnyPropertyType, 989 | &type, &format, &nitems, &after, 990 | (unsigned char **)&data); 991 | 992 | if (result == Success) { 993 | if (nitems == 4 && after == 0) { 994 | extents = (long *)data; 995 | *left = 996 | (unsigned int)extents[0]; 997 | *right = 998 | (unsigned int)extents[1]; 999 | *top = 1000 | (unsigned int)extents[2]; 1001 | *bottom = 1002 | (unsigned int)extents[3]; 1003 | } 1004 | XFree(data); 1005 | } 1006 | } 1007 | 1008 | static Bool 1009 | win_paint_needed(win* w, CompRect* ignore_reg){ 1010 | // if invisible, ignore it 1011 | if (unlikely(w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 1012 | || w->a.x >= root_width || w->a.y >= root_height)) { 1013 | return False; 1014 | } 1015 | // Unmapped, destroyed or translucent windows must not contribute to the ignore region. 1016 | // Same applies to override_redirect windows, which some screenshooter apps employ 1017 | // (s. e.g. xfce4-screenshooter 1018 | // screenshooter-capture.c::get_rectangle_screenshot_composited ) 1019 | if (w->a.map_state != IsViewable || w->destroyed || w->opacity != OPAQUE || 1020 | w->a.override_redirect){ 1021 | return True; 1022 | } 1023 | CompRect w_rect = {.x1 = w->a.x, .y1 = w->a.y, 1024 | .x2 = w->a.x + w->a.width, .y2 = w->a.y + w->a.height, 1025 | .w = w->a.width , .h = w->a.height }; 1026 | return rect_paint_needed(ignore_reg, &w_rect); 1027 | } 1028 | 1029 | static void 1030 | paint_all(Display *dpy, XserverRegion region) { 1031 | win *w; 1032 | win *t = 0; 1033 | Bool ignore_region_is_dirty = g_paint_ignore_region_is_dirty; 1034 | g_paint_ignore_region_is_dirty = False; 1035 | 1036 | #if MONITOR_REPAINT 1037 | root_buffer = root_picture; 1038 | #else 1039 | if (unlikely(!root_buffer)) { 1040 | Pixmap rootPixmap = XCreatePixmap( 1041 | dpy, root, root_width, root_height, 1042 | DefaultDepth(dpy, g_screen)); 1043 | 1044 | root_buffer = XRenderCreatePicture(dpy, rootPixmap, 1045 | XRenderFindVisualFormat(dpy, DefaultVisual(dpy, g_screen)), 1046 | 0, 0); 1047 | 1048 | XFreePixmap(dpy, rootPixmap); 1049 | } 1050 | #endif 1051 | 1052 | XFixesSetPictureClipRegion(dpy, root_picture, 0, 0, region); 1053 | 1054 | #if MONITOR_REPAINT 1055 | XRenderComposite( 1056 | dpy, PictOpSrc, black_picture, None, 1057 | root_picture, 0, 0, 0, 0, 0, 0, 1058 | root_width, root_height); 1059 | #endif 1060 | 1061 | #if DEBUG_REPAINT 1062 | printf("paint:"); 1063 | #endif 1064 | 1065 | CompRect ignore_reg = {0}; 1066 | for (w = list; w; w = w->next) { 1067 | // Don't do this here, otherwise we get artifacts after move. 1068 | // if (w->need_configure){ 1069 | // do_configure_win(dpy, w); 1070 | // } 1071 | 1072 | #if CAN_DO_USABLE 1073 | if (!w->usable) continue; 1074 | #endif 1075 | 1076 | /* never painted, ignore it */ 1077 | if (likely(!w->damaged)) continue; 1078 | 1079 | // Note that undamaged windows should not contribute to the ignore 1080 | // region. Otherwise VBoxManager makes other windows disappear during startup. 1081 | if(unlikely(ignore_region_is_dirty)){ 1082 | // maybe_todo: pass only clipped rects, actually visible on screen. 1083 | // Now we may choose the ignore region from a big window which 1084 | // resides largely outside the screen. 1085 | w->paint_needed = win_paint_needed(w, &ignore_reg); 1086 | } 1087 | if(!w->paint_needed) continue; 1088 | 1089 | if (!w->picture) { 1090 | XRenderPictureAttributes pa; 1091 | XRenderPictFormat *format; 1092 | Drawable draw = w->id; 1093 | 1094 | #if HAS_NAME_WINDOW_PIXMAP 1095 | if (has_name_pixmap && !w->pixmap) { 1096 | set_ignore(dpy, NextRequest(dpy)); 1097 | w->pixmap = XCompositeNameWindowPixmap(dpy, w->id); 1098 | } 1099 | if (w->pixmap) draw = w->pixmap; 1100 | #endif 1101 | 1102 | format = XRenderFindVisualFormat(dpy, w->a.visual); 1103 | pa.subwindow_mode = IncludeInferiors; 1104 | w->picture = XRenderCreatePicture( 1105 | dpy, draw, format, CPSubwindowMode, &pa); 1106 | } 1107 | 1108 | #if DEBUG_REPAINT 1109 | printf(" 0x%x", w->id); 1110 | #endif 1111 | 1112 | if (clip_changed) { 1113 | if (w->border_size) { 1114 | set_ignore(dpy, NextRequest(dpy)); 1115 | XFixesDestroyRegion(dpy, w->border_size); 1116 | w->border_size = None; 1117 | } 1118 | win_extents(dpy, w); 1119 | } 1120 | 1121 | if (!w->border_size) { 1122 | w->border_size = border_size (dpy, w); 1123 | } 1124 | 1125 | if (unlikely(!w->extents)) { 1126 | win_extents(dpy, w); 1127 | } 1128 | 1129 | if (w->mode == WINDOW_SOLID && !HAS_FRAME_OPACITY(w)) { 1130 | int x, y, wid, hei; 1131 | 1132 | #if HAS_NAME_WINDOW_PIXMAP 1133 | x = w->a.x; 1134 | y = w->a.y; 1135 | wid = w->a.width + w->a.border_width * 2; 1136 | hei = w->a.height + w->a.border_width * 2; 1137 | #else 1138 | x = w->a.x + w->a.border_width; 1139 | y = w->a.y + w->a.border_width; 1140 | wid = w->a.width; 1141 | hei = w->a.height; 1142 | #endif 1143 | 1144 | set_ignore(dpy, NextRequest(dpy)); 1145 | XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, region); 1146 | 1147 | set_ignore(dpy, NextRequest(dpy)); 1148 | XFixesSubtractRegion(dpy, region, region, w->border_size); 1149 | 1150 | XRenderComposite( 1151 | dpy, PictOpSrc, w->picture, 1152 | None, root_buffer, 0, 0, 0, 0, 1153 | x, y, wid, hei); 1154 | } 1155 | 1156 | XFixesCopyRegion(dpy, w->border_clip, region); 1157 | 1158 | w->prev_trans = t; 1159 | t = w; 1160 | } 1161 | 1162 | #if DEBUG_REPAINT 1163 | printf("\n"); 1164 | fflush(stdout); 1165 | #endif 1166 | 1167 | XFixesSetPictureClipRegion(dpy, 1168 | root_buffer, 0, 0, region); 1169 | paint_root(dpy); 1170 | 1171 | for (w = t; w; w = w->prev_trans) { 1172 | XFixesSetPictureClipRegion(dpy, 1173 | root_buffer, 0, 0, w->border_clip); 1174 | 1175 | if(w->shadow_type == SHADOW_YES) { 1176 | XRenderComposite( 1177 | dpy, PictOpOver, cshadow_picture, w->shadow, 1178 | root_buffer, 0, 0, 0, 0, 1179 | w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, 1180 | w->shadow_width, w->shadow_height); 1181 | } 1182 | 1183 | if (w->opacity != OPAQUE && !w->alpha_pict) { 1184 | w->alpha_pict = solid_picture( 1185 | dpy, False, (double)w->opacity / OPAQUE, 0, 0, 0); 1186 | } 1187 | if (HAS_FRAME_OPACITY(w) && !w->alpha_border_pict) { 1188 | w->alpha_border_pict = solid_picture( 1189 | dpy, False, frame_opacity, 0, 0, 0); 1190 | } 1191 | 1192 | if (w->mode != WINDOW_SOLID || HAS_FRAME_OPACITY(w)) { 1193 | int x, y, wid, hei; 1194 | // 2024-11-26: Without the next two lines, the Microsoft-Teams screen-share 1195 | // window has a broken frame instead of a shadow, with a "startup-frozen" 1196 | // picture. Inspired by xcompmgr's commit 5a7d139f (2012-08-11). 1197 | XFixesIntersectRegion(dpy, w->border_clip, w->border_clip, w->border_size); 1198 | XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, w->border_clip); 1199 | 1200 | #if HAS_NAME_WINDOW_PIXMAP 1201 | x = w->a.x; 1202 | y = w->a.y; 1203 | wid = w->a.width + w->a.border_width * 2; 1204 | hei = w->a.height + w->a.border_width * 2; 1205 | #else 1206 | x = w->a.x + w->a.border_width; 1207 | y = w->a.y + w->a.border_width; 1208 | wid = w->a.width; 1209 | hei = w->a.height; 1210 | #endif 1211 | 1212 | set_ignore(dpy, NextRequest(dpy)); 1213 | 1214 | if (!HAS_FRAME_OPACITY(w)) { 1215 | XRenderComposite( 1216 | dpy, PictOpOver, w->picture, w->alpha_pict, 1217 | root_buffer, 0, 0, 0, 0, x, y, wid, hei); 1218 | } else { 1219 | /* TODO - clean me */ 1220 | unsigned int t = w->top_width; 1221 | unsigned int l = w->left_width; 1222 | unsigned int b = w->bottom_width; 1223 | unsigned int r = w->right_width; 1224 | 1225 | /* top */ 1226 | XRenderComposite( 1227 | dpy, PictOpOver, w->picture, w->alpha_border_pict, root_buffer, 1228 | 0, 0, 0, 0, x, y, wid, t); 1229 | 1230 | /* left */ 1231 | XRenderComposite( 1232 | dpy, PictOpOver, w->picture, w->alpha_border_pict, root_buffer, 1233 | 0, t, 0, t, x, y + t, l, hei - t); 1234 | 1235 | /* bottom */ 1236 | XRenderComposite( 1237 | dpy, PictOpOver, w->picture, w->alpha_border_pict, root_buffer, 1238 | l, hei - b, l, hei - b, x + l, y + hei - b, wid - l - r, b); 1239 | 1240 | /* right */ 1241 | XRenderComposite( 1242 | dpy, PictOpOver, w->picture, w->alpha_border_pict, root_buffer, 1243 | wid - r, t, wid - r, t, x + wid - r, y + t, r, hei - t); 1244 | 1245 | /* body */ 1246 | XRenderComposite( 1247 | dpy, PictOpOver, w->picture, w->alpha_pict, root_buffer, 1248 | l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); 1249 | } 1250 | } 1251 | } 1252 | 1253 | #if ! MONITOR_REPAINT 1254 | XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, None); 1255 | XRenderComposite( 1256 | dpy, PictOpSrc, root_buffer, None, 1257 | root_picture, 0, 0, 0, 0, 1258 | 0, 0, root_width, root_height); 1259 | #endif // ! MONITOR_REPAINT 1260 | } 1261 | 1262 | static void 1263 | add_damage(Display *dpy, XserverRegion damage) { 1264 | if (all_damage_is_dirty) { 1265 | XFixesUnionRegion(dpy, all_damage, all_damage, damage); 1266 | } else { 1267 | XFixesCopyRegion(dpy, all_damage, damage); 1268 | all_damage_is_dirty = True; 1269 | } 1270 | } 1271 | 1272 | static void 1273 | repair_win(Display *dpy, win *w) { 1274 | XserverRegion parts; 1275 | 1276 | if (!w->damaged) { 1277 | parts = win_extents(dpy, w); 1278 | set_ignore(dpy, NextRequest(dpy)); 1279 | XDamageSubtract(dpy, w->damage, None, None); 1280 | } else { 1281 | parts = g_xregion_tmp; 1282 | set_ignore(dpy, NextRequest(dpy)); 1283 | XDamageSubtract(dpy, w->damage, None, parts); 1284 | XFixesTranslateRegion(dpy, parts, 1285 | w->a.x + w->a.border_width, 1286 | w->a.y + w->a.border_width); 1287 | } 1288 | 1289 | add_damage(dpy, parts); 1290 | w->damaged = 1; 1291 | } 1292 | 1293 | #if 0 1294 | static const char* 1295 | wintype_name(wintype type) { 1296 | const char *t; 1297 | 1298 | switch (type) { 1299 | case WINTYPE_DESKTOP: 1300 | t = "desktop"; 1301 | break; 1302 | case WINTYPE_DOCK: 1303 | t = "dock"; 1304 | break; 1305 | case WINTYPE_TOOLBAR: 1306 | t = "toolbar"; 1307 | break; 1308 | case WINTYPE_MENU: 1309 | t = "menu"; 1310 | break; 1311 | case WINTYPE_UTILITY: 1312 | t = "utility"; 1313 | break; 1314 | case WINTYPE_SPLASH: 1315 | t = "slash"; 1316 | break; 1317 | case WINTYPE_DIALOG: 1318 | t = "dialog"; 1319 | break; 1320 | case WINTYPE_NORMAL: 1321 | t = "normal"; 1322 | break; 1323 | case WINTYPE_DROPDOWN_MENU: 1324 | t = "dropdown"; 1325 | break; 1326 | case WINTYPE_POPUP_MENU: 1327 | t = "popup"; 1328 | break; 1329 | case WINTYPE_TOOLTIP: 1330 | t = "tooltip"; 1331 | break; 1332 | case WINTYPE_NOTIFY: 1333 | t = "notification"; 1334 | break; 1335 | case WINTYPE_COMBO: 1336 | t = "combo"; 1337 | break; 1338 | case WINTYPE_DND: 1339 | t = "dnd"; 1340 | break; 1341 | default: 1342 | t = "unknown"; 1343 | break; 1344 | } 1345 | 1346 | return t; 1347 | } 1348 | #endif 1349 | 1350 | static wintype 1351 | get_wintype_prop(Display * dpy, Window w) { 1352 | Atom actual; 1353 | int format; 1354 | unsigned long n, left, off; 1355 | unsigned char *data; 1356 | 1357 | off = 0; 1358 | 1359 | do { 1360 | set_ignore(dpy, NextRequest(dpy)); 1361 | 1362 | int result = XGetWindowProperty( 1363 | dpy, w, atom_win_type, off, 1L, False, XA_ATOM, 1364 | &actual, &format, &n, &left, &data); 1365 | 1366 | if (unlikely(result != Success)) break; 1367 | 1368 | if (likely(data != None)) { 1369 | int i; 1370 | for (i = 1; i < NUM_WINTYPES; ++i) { 1371 | Atom a; 1372 | memcpy(&a, data, sizeof(Atom)); 1373 | if (a == win_type[i]) { 1374 | /* known type */ 1375 | XFree((void *) data); 1376 | return i; 1377 | } 1378 | } 1379 | XFree((void *) data); 1380 | } 1381 | ++off; 1382 | } while (left >= 4); 1383 | 1384 | return WINTYPE_UNKNOWN; 1385 | } 1386 | 1387 | static wintype 1388 | determine_wintype(Display *dpy, Window w, Window top) { 1389 | Window root_return, parent_return; 1390 | Window *children = NULL; 1391 | unsigned int nchildren, i; 1392 | wintype type; 1393 | 1394 | type = get_wintype_prop(dpy, w); 1395 | if (type != WINTYPE_UNKNOWN) return type; 1396 | 1397 | set_ignore(dpy, NextRequest(dpy)); 1398 | if (unlikely(!XQueryTree(dpy, w, &root_return, &parent_return, 1399 | &children, &nchildren))) { 1400 | goto free_out; 1401 | } 1402 | 1403 | for (i = 0; i < nchildren; i++) { 1404 | type = determine_wintype(dpy, children[i], top); 1405 | if (type != WINTYPE_UNKNOWN) goto free_out; 1406 | } 1407 | 1408 | if (w != top) { 1409 | type = WINTYPE_UNKNOWN; 1410 | } else { 1411 | type = WINTYPE_NORMAL; 1412 | } 1413 | 1414 | free_out: 1415 | if (children) XFree((void *)children); 1416 | return type; 1417 | } 1418 | 1419 | static unsigned int 1420 | get_opacity_prop(Display *dpy, win *w, unsigned int def); 1421 | 1422 | static void 1423 | handle_ConfigureNotify(Display *dpy, XConfigureEvent *ce); 1424 | 1425 | static void 1426 | map_win(Display *dpy, Window id, 1427 | unsigned long sequence, Bool fade) { 1428 | win *w = find_win(dpy, id); 1429 | 1430 | if (unlikely(!w)) return; 1431 | 1432 | w->a.map_state = IsViewable; 1433 | w->window_type = determine_wintype(dpy, w->id, w->id); 1434 | 1435 | if (! w->border_clip) { 1436 | w->border_clip = XFixesCreateRegion(dpy, 0, 0); 1437 | } 1438 | 1439 | #if 0 1440 | printf("window 0x%x type %s\n", 1441 | w->id, wintype_name(w->window_type)); 1442 | #endif 1443 | 1444 | /* select before reading the property 1445 | so that no property changes are lost */ 1446 | XSelectInput(dpy, id, PropertyChangeMask | FocusChangeMask); 1447 | 1448 | // this causes problems for inactive transparency 1449 | //w->opacity = get_opacity_prop(dpy, w, OPAQUE); 1450 | 1451 | determine_mode(dpy, w); 1452 | 1453 | #if CAN_DO_USABLE 1454 | w->damage_bounds.x = w->damage_bounds.y = 0; 1455 | w->damage_bounds.width = w->damage_bounds.height = 0; 1456 | #endif 1457 | w->damaged = 0; 1458 | w->paint_needed = True; 1459 | 1460 | if (fade && win_type_fade[w->window_type]) { 1461 | set_fade( 1462 | dpy, w, 0, get_opacity_percent(dpy, w), 1463 | fade_in_step, 0, True, True); 1464 | } 1465 | 1466 | set_paint_ignore_region_dirty(); 1467 | 1468 | /* if any configure events happened while 1469 | the window was unmapped, then configure 1470 | the window to its correct place */ 1471 | // if (w->need_configure) { 1472 | // handle_ConfigureNotify(dpy, &w->queue_configure); 1473 | // } 1474 | } 1475 | 1476 | static void 1477 | finish_unmap_win(Display *dpy, win *w) { 1478 | w->damaged = 0; 1479 | #if CAN_DO_USABLE 1480 | w->usable = False; 1481 | #endif 1482 | 1483 | if (w->extents != None) { 1484 | add_damage(dpy, w->extents); 1485 | } 1486 | 1487 | #if HAS_NAME_WINDOW_PIXMAP 1488 | if (w->pixmap) { 1489 | XFreePixmap(dpy, w->pixmap); 1490 | w->pixmap = None; 1491 | } 1492 | #endif 1493 | 1494 | if (w->picture) { 1495 | set_ignore(dpy, NextRequest(dpy)); 1496 | XRenderFreePicture(dpy, w->picture); 1497 | w->picture = None; 1498 | } 1499 | 1500 | if (w->border_size) { 1501 | set_ignore(dpy, NextRequest(dpy)); 1502 | XFixesDestroyRegion(dpy, w->border_size); 1503 | w->border_size = None; 1504 | } 1505 | 1506 | if (w->shadow) { 1507 | XRenderFreePicture(dpy, w->shadow); 1508 | w->shadow = None; 1509 | } 1510 | 1511 | clip_changed = True; 1512 | } 1513 | 1514 | #if HAS_NAME_WINDOW_PIXMAP 1515 | static void 1516 | unmap_callback(Display *dpy, win *w) { 1517 | finish_unmap_win(dpy, w); 1518 | } 1519 | #endif 1520 | 1521 | static void 1522 | unmap_win(Display *dpy, Window id, Bool fade) { 1523 | win *w = find_win(dpy, id); 1524 | 1525 | if (!w) return; 1526 | 1527 | /* don't care about properties anymore */ 1528 | set_ignore(dpy, NextRequest(dpy)); 1529 | XSelectInput(dpy, w->id, 0); 1530 | 1531 | w->a.map_state = IsUnmapped; 1532 | set_paint_ignore_region_dirty(); 1533 | 1534 | 1535 | #if HAS_NAME_WINDOW_PIXMAP 1536 | if (w->pixmap && fade && win_type_fade[w->window_type]) { 1537 | set_fade(dpy, w, w->opacity * 1.0 / OPAQUE, 0.0, 1538 | fade_out_step, unmap_callback, False, True); 1539 | } else 1540 | #endif 1541 | finish_unmap_win(dpy, w); 1542 | } 1543 | 1544 | static bool is_gtk_frame_extent(Display *dpy, Window w){ 1545 | Atom type; 1546 | int format; 1547 | unsigned long nitems, after; 1548 | unsigned char *data = NULL; 1549 | int result; 1550 | 1551 | result = XGetWindowProperty(dpy, w, atom_gtk_frame_extents, 0, LONG_MAX, 1552 | false, XA_CARDINAL, &type, &format, &nitems, &after, (unsigned char **)&data); 1553 | if (result == Success && data!=NULL) { 1554 | XFree((void *)data); 1555 | return nitems == 4 ; 1556 | } 1557 | return false; 1558 | } 1559 | 1560 | /* Get the opacity prop from window 1561 | not found: default 1562 | otherwise the value 1563 | */ 1564 | static unsigned int 1565 | get_opacity_prop(Display *dpy, win *w, unsigned int def) { 1566 | Atom actual; 1567 | int format; 1568 | unsigned long n, left; 1569 | 1570 | unsigned char *data; 1571 | int result = XGetWindowProperty( 1572 | dpy, w->id, atom_opacity, 0L, 1L, False, 1573 | XA_CARDINAL, &actual, &format, &n, &left, &data); 1574 | 1575 | if (result == Success && data != NULL) { 1576 | unsigned int i; 1577 | memcpy(&i, data, sizeof(unsigned int)); 1578 | XFree((void *)data); 1579 | return i; 1580 | } 1581 | 1582 | return def; 1583 | } 1584 | 1585 | /* 1586 | * Get the opacity property from the window in a percent format 1587 | * not found: default 1588 | * otherwise: the value 1589 | */ 1590 | 1591 | static double 1592 | get_opacity_percent(Display *dpy, win *w) { 1593 | double def = win_type_opacity[w->window_type]; 1594 | unsigned int opacity = 1595 | get_opacity_prop(dpy, w, (unsigned int)(OPAQUE * def)); 1596 | 1597 | return opacity * 1.0 / OPAQUE; 1598 | } 1599 | 1600 | static void 1601 | determine_mode(Display *dpy, win *w) { 1602 | int mode; 1603 | XRenderPictFormat *format; 1604 | 1605 | /* if trans prop == -1 fall back on previous tests*/ 1606 | 1607 | if (w->alpha_pict) { 1608 | XRenderFreePicture(dpy, w->alpha_pict); 1609 | w->alpha_pict = None; 1610 | } 1611 | 1612 | if (w->alpha_border_pict) { 1613 | XRenderFreePicture(dpy, w->alpha_border_pict); 1614 | w->alpha_border_pict = None; 1615 | } 1616 | 1617 | if (w->shadow_pict) { 1618 | XRenderFreePicture(dpy, w->shadow_pict); 1619 | w->shadow_pict = None; 1620 | } 1621 | 1622 | if (w->a.class == InputOnly) { 1623 | format = 0; 1624 | } else { 1625 | format = XRenderFindVisualFormat(dpy, w->a.visual); 1626 | } 1627 | 1628 | if (format && format->type == PictTypeDirect 1629 | && format->direct.alphaMask) { 1630 | mode = WINDOW_ARGB; 1631 | } else if (w->opacity != OPAQUE) { 1632 | mode = WINDOW_TRANS; 1633 | } else { 1634 | mode = WINDOW_SOLID; 1635 | } 1636 | 1637 | w->mode = mode; 1638 | 1639 | if (w->extents) { 1640 | add_damage(dpy, w->extents); 1641 | } 1642 | } 1643 | 1644 | static void 1645 | set_opacity(Display *dpy, win *w, unsigned long opacity) { 1646 | if (fade_trans) { 1647 | double old_opacity = (double)w->opacity / OPAQUE; 1648 | w->opacity = opacity; 1649 | set_fade(dpy, w, old_opacity, 1650 | (double)w->opacity / OPAQUE, 1651 | fade_out_step, 0, True, False); 1652 | } else { 1653 | w->opacity = opacity; 1654 | determine_mode(dpy, w); 1655 | if (w->shadow) { 1656 | // rebuild the shadow 1657 | XRenderFreePicture(dpy, w->shadow); 1658 | w->shadow = None; 1659 | win_extents(dpy, w); 1660 | } 1661 | } 1662 | set_paint_ignore_region_dirty(); 1663 | } 1664 | 1665 | static void 1666 | add_win(Display *dpy, Window id, Window prev) { 1667 | win *new = calloc(1, sizeof(win)); 1668 | win **p; 1669 | 1670 | if (unlikely(!new)) return; 1671 | 1672 | if (prev) { 1673 | for (p = &list; *p; p = &(*p)->next) { 1674 | if ((*p)->id == prev && !(*p)->destroyed) 1675 | break; 1676 | } 1677 | } else { 1678 | p = &list; 1679 | } 1680 | 1681 | new->id = id; 1682 | set_ignore(dpy, NextRequest(dpy)); 1683 | 1684 | if (unlikely(!XGetWindowAttributes(dpy, id, &new->a))) { 1685 | free(new); 1686 | return; 1687 | } 1688 | 1689 | #if HAS_NAME_WINDOW_PIXMAP 1690 | new->pixmap = None; 1691 | #endif 1692 | new->picture = None; 1693 | 1694 | if (new->a.class == InputOnly) { 1695 | // we used calloc, so no need to set zeroes 1696 | // new->damage_sequence = 0; 1697 | new->damage = None; 1698 | } else { 1699 | new->damage_sequence = NextRequest(dpy); 1700 | set_ignore(dpy, NextRequest(dpy)); 1701 | new->damage = XDamageCreate(dpy, id, XDamageReportNonEmpty); 1702 | } 1703 | 1704 | new->alpha_pict = None; 1705 | new->alpha_border_pict = None; 1706 | new->shadow_pict = None; 1707 | new->border_size = None; 1708 | new->extents = None; 1709 | new->shadow = None; 1710 | 1711 | // we used calloc, so no need to set zeroes 1712 | // new->shadow_dx = 0; 1713 | // new->shadow_dy = 0; 1714 | // new->shadow_width = 0; 1715 | // new->shadow_height = 0; 1716 | // new->window_type = WINTYPE_UNKNOWN; 1717 | // new->prev_trans = 0; 1718 | // new->left_width = 0; 1719 | // new->right_width = 0; 1720 | // new->top_width = 0; 1721 | // new->bottom_width = 0; 1722 | // new->need_configure = False; 1723 | // new->destroyed = False; 1724 | // new->damaged = 0; 1725 | // #if CAN_DO_USABLE 1726 | // new->usable = False; 1727 | // #endif 1728 | 1729 | new->opacity = OPAQUE; 1730 | 1731 | new->border_clip = None; 1732 | get_frame_extents(dpy, id, 1733 | &new->left_width, &new->right_width, 1734 | &new->top_width, &new->bottom_width); 1735 | 1736 | new->next = *p; 1737 | *p = new; 1738 | 1739 | if (new->a.map_state == IsViewable) { 1740 | new->window_type = determine_wintype(dpy, id, id); 1741 | if (inactive_opacity && IS_NORMAL_WIN(new)) { 1742 | new->opacity = INACTIVE_OPACITY; 1743 | } 1744 | map_win(dpy, id, new->damage_sequence - 1, True); 1745 | } 1746 | } 1747 | 1748 | static void 1749 | set_paint_ignore_region_dirty(void){ 1750 | g_paint_ignore_region_is_dirty = True; 1751 | } 1752 | 1753 | void 1754 | restack_win(Display *dpy, win *w, Window new_above) { 1755 | Window old_above; 1756 | 1757 | if (w->next) { 1758 | old_above = w->next->id; 1759 | } else { 1760 | old_above = None; 1761 | } 1762 | 1763 | if (old_above != new_above) { 1764 | win **prev; 1765 | 1766 | /* unhook */ 1767 | for (prev = &list; *prev; prev = &(*prev)->next) { 1768 | if ((*prev) == w) break; 1769 | } 1770 | 1771 | *prev = w->next; 1772 | 1773 | /* rehook */ 1774 | for (prev = &list; *prev; prev = &(*prev)->next) { 1775 | if ((*prev)->id == new_above && !(*prev)->destroyed) 1776 | break; 1777 | } 1778 | 1779 | w->next = *prev; 1780 | *prev = w; 1781 | } 1782 | } 1783 | 1784 | static void 1785 | do_configure_win(Display *dpy, win* w){ 1786 | XConfigureEvent* ce = &w->queue_configure; 1787 | 1788 | w->need_configure = False; 1789 | w->a.x = ce->x; 1790 | w->a.y = ce->y; 1791 | if (w->configure_size_changed) { 1792 | 1793 | #if HAS_NAME_WINDOW_PIXMAP 1794 | if (w->pixmap) { 1795 | XFreePixmap(dpy, w->pixmap); 1796 | w->pixmap = None; 1797 | if (w->picture) { 1798 | XRenderFreePicture(dpy, w->picture); 1799 | w->picture = None; 1800 | } 1801 | } 1802 | #endif 1803 | 1804 | if (w->shadow) { 1805 | XRenderFreePicture(dpy, w->shadow); 1806 | w->shadow = None; 1807 | } 1808 | } 1809 | 1810 | w->a.width = ce->width; 1811 | w->a.height = ce->height; 1812 | w->a.border_width = ce->border_width; 1813 | 1814 | if (w->a.map_state != IsUnmapped 1815 | #if CAN_DO_USABLE 1816 | && w->usable 1817 | #endif 1818 | ) { 1819 | // both, the old and new window position/size are damaged. 1820 | if (likely(w->extents != None)) { 1821 | add_damage(dpy, w->extents); 1822 | } 1823 | add_damage(dpy, win_extents(dpy, w)); 1824 | } 1825 | 1826 | clip_changed = True; 1827 | w->a.override_redirect = ce->override_redirect; 1828 | w->configure_size_changed = false; 1829 | set_paint_ignore_region_dirty(); 1830 | } 1831 | 1832 | Bool g_configure_needed = False; 1833 | 1834 | static void 1835 | handle_ConfigureNotify(Display *dpy, XConfigureEvent *ce) { 1836 | win *w = find_win(dpy, ce->window); 1837 | 1838 | if (unlikely(!w)) { 1839 | if (ce->window == root) { 1840 | if (root_buffer) { 1841 | XRenderFreePicture(dpy, root_buffer); 1842 | root_buffer = None; 1843 | } 1844 | root_width = ce->width; 1845 | root_height = ce->height; 1846 | } 1847 | return; 1848 | } 1849 | // save the configure event for later. While on the one hand, we're only 1850 | // interested in the final position and size (after timeout), a change in size 1851 | // invalidates the pixmap, so remember any resize event. 1852 | g_configure_needed = True; 1853 | w->need_configure = True; 1854 | if (w->a.width != ce->width || w->a.height != ce->height) { 1855 | w->configure_size_changed = true; 1856 | } 1857 | 1858 | w->queue_configure = *ce; 1859 | 1860 | // w->a.override_redirect = ce->override_redirect; 1861 | restack_win(dpy, w, ce->above); 1862 | } 1863 | 1864 | static void 1865 | circulate_win(Display *dpy, XCirculateEvent *ce) { 1866 | win *w = find_win(dpy, ce->window); 1867 | Window new_above; 1868 | 1869 | if (!w) return; 1870 | 1871 | if (ce->place == PlaceOnTop) { 1872 | new_above = list->id; 1873 | } else { 1874 | new_above = None; 1875 | } 1876 | 1877 | restack_win(dpy, w, new_above); 1878 | clip_changed = True; 1879 | } 1880 | 1881 | static void 1882 | finish_destroy_win(Display *dpy, Window id) { 1883 | win **prev, *w; 1884 | 1885 | for (prev = &list; (w = *prev); prev = &w->next) { 1886 | if (w->id == id && w->destroyed) { 1887 | finish_unmap_win(dpy, w); 1888 | *prev = w->next; 1889 | 1890 | if (w->alpha_pict) { 1891 | XRenderFreePicture(dpy, w->alpha_pict); 1892 | w->alpha_pict = None; 1893 | } 1894 | 1895 | if (w->alpha_border_pict) { 1896 | XRenderFreePicture(dpy, w->alpha_border_pict); 1897 | w->alpha_border_pict = None; 1898 | } 1899 | 1900 | if (w->shadow_pict) { 1901 | XRenderFreePicture(dpy, w->shadow_pict); 1902 | w->shadow_pict = None; 1903 | } 1904 | 1905 | /* fix leak, from freedesktop repo */ 1906 | if (w->shadow) { 1907 | XRenderFreePicture (dpy, w->shadow); 1908 | w->shadow = None; 1909 | } 1910 | 1911 | if (w->damage != None) { 1912 | set_ignore(dpy, NextRequest(dpy)); 1913 | XDamageDestroy(dpy, w->damage); 1914 | w->damage = None; 1915 | } 1916 | 1917 | cleanup_fade(dpy, w); 1918 | 1919 | if (w->border_clip) { 1920 | XFixesDestroyRegion(dpy, w->border_clip); 1921 | w->border_clip = None; 1922 | } 1923 | if(w->extents){ 1924 | XFixesDestroyRegion(dpy, w->extents); 1925 | w->extents = None; 1926 | } 1927 | free(w); 1928 | break; 1929 | } 1930 | } 1931 | } 1932 | 1933 | #if HAS_NAME_WINDOW_PIXMAP 1934 | static void 1935 | destroy_callback(Display *dpy, win *w) { 1936 | finish_destroy_win(dpy, w->id); 1937 | } 1938 | #endif 1939 | 1940 | static void 1941 | destroy_win(Display *dpy, Window id, Bool fade) { 1942 | win *w = find_win(dpy, id); 1943 | 1944 | if (w) w->destroyed = True; 1945 | 1946 | set_paint_ignore_region_dirty(); 1947 | 1948 | #if HAS_NAME_WINDOW_PIXMAP 1949 | if (w && w->pixmap && fade && win_type_fade[w->window_type]) { 1950 | set_fade(dpy, w, w->opacity * 1.0 / OPAQUE, 1951 | 0.0, fade_out_step, destroy_callback, 1952 | False, True); 1953 | } else 1954 | #endif 1955 | { 1956 | finish_destroy_win(dpy, id); 1957 | } 1958 | } 1959 | 1960 | #if 0 1961 | static void 1962 | dump_win(win *w) { 1963 | printf("\t%08lx: %d x %d + %d + %d(%d)\n", w->id, 1964 | w->a.width, w->a.height, 1965 | w->a.x, w->a.y, w->a.border_width); 1966 | } 1967 | 1968 | 1969 | static void 1970 | dump_wins(void) { 1971 | win *w; 1972 | 1973 | printf("windows:\n"); 1974 | for (w = list; w; w = w->next) { 1975 | dump_win(w); 1976 | } 1977 | } 1978 | #endif 1979 | 1980 | static void 1981 | damage_win(Display *dpy, XDamageNotifyEvent *de) { 1982 | win *w = find_win(dpy, de->drawable); 1983 | 1984 | if (unlikely(!w)) return; 1985 | 1986 | #if CAN_DO_USABLE 1987 | if (!w->usable) { 1988 | if (w->damage_bounds.width == 0 || w->damage_bounds.height == 0) { 1989 | w->damage_bounds = de->area; 1990 | } else { 1991 | if (de->area.x < w->damage_bounds.x) { 1992 | w->damage_bounds.width += (w->damage_bounds.x - de->area.x); 1993 | w->damage_bounds.x = de->area.x; 1994 | } 1995 | if (de->area.y < w->damage_bounds.y) { 1996 | w->damage_bounds.height += (w->damage_bounds.y - de->area.y); 1997 | w->damage_bounds.y = de->area.y; 1998 | } 1999 | if (de->area.x + de->area.width 2000 | > w->damage_bounds.x + w->damage_bounds.width) { 2001 | w->damage_bounds.width = 2002 | de->area.x + de->area.width - w->damage_bounds.x; 2003 | } 2004 | if (de->area.y + de->area.height 2005 | > w->damage_bounds.y + w->damage_bounds.height) { 2006 | w->damage_bounds.height = 2007 | de->area.y + de->area.height - w->damage_bounds.y; 2008 | } 2009 | } 2010 | 2011 | #if 0 2012 | printf("unusable damage %d, %d: %d x %d bounds %d, %d: %d x %d\n", 2013 | de->area.x, 2014 | de->area.y, 2015 | de->area.width, 2016 | de->area.height, 2017 | w->damage_bounds.x, 2018 | w->damage_bounds.y, 2019 | w->damage_bounds.width, 2020 | w->damage_bounds.height); 2021 | #endif 2022 | 2023 | if (w->damage_bounds.x <= 0 2024 | && w->damage_bounds.y <= 0 2025 | && w->a.width <= w->damage_bounds.x + w->damage_bounds.width 2026 | && w->a.height <= w->damage_bounds.y + w->damage_bounds.height) { 2027 | clip_changed = True; 2028 | if (win_type_fade[w->window_type]) { 2029 | set_fade(dpy, w, 0, get_opacity_percent(dpy, w), 2030 | fade_in_step, 0, True, True); 2031 | } 2032 | w->usable = True; 2033 | } 2034 | } 2035 | 2036 | if (w->usable) 2037 | #endif 2038 | repair_win(dpy, w); 2039 | } 2040 | 2041 | static int 2042 | error(Display *dpy, XErrorEvent *ev) { 2043 | int o; 2044 | const char *name = "Unknown"; 2045 | 2046 | if (should_ignore(dpy, ev->serial)) { 2047 | return 0; 2048 | } 2049 | 2050 | if (ev->request_code == composite_opcode 2051 | && ev->minor_code == X_CompositeRedirectSubwindows) { 2052 | fprintf(stderr, "Another composite manager is already running\n"); 2053 | exit(1); 2054 | } 2055 | 2056 | o = ev->error_code - xfixes_error; 2057 | switch (o) { 2058 | case BadRegion: 2059 | name = "BadRegion"; 2060 | break; 2061 | default: 2062 | break; 2063 | } 2064 | 2065 | o = ev->error_code - damage_error; 2066 | switch (o) { 2067 | case BadDamage: 2068 | name = "BadDamage"; 2069 | break; 2070 | default: 2071 | break; 2072 | } 2073 | 2074 | o = ev->error_code - render_error; 2075 | switch (o) { 2076 | case BadPictFormat: 2077 | name ="BadPictFormat"; 2078 | break; 2079 | case BadPicture: 2080 | name ="BadPicture"; 2081 | break; 2082 | case BadPictOp: 2083 | name ="BadPictOp"; 2084 | break; 2085 | case BadGlyphSet: 2086 | name ="BadGlyphSet"; 2087 | break; 2088 | case BadGlyph: 2089 | name ="BadGlyph"; 2090 | break; 2091 | default: 2092 | break; 2093 | } 2094 | 2095 | fprintf(stderr, "error %d (%s) request %d minor %d serial %lu\n", 2096 | ev->error_code, name, ev->request_code, 2097 | ev->minor_code, ev->serial); 2098 | 2099 | /* abort(); this is just annoying to most people */ 2100 | return 0; 2101 | } 2102 | 2103 | static void 2104 | expose_root(Display *dpy, Window root, XRectangle *rects, int nrects) { 2105 | XFixesSetRegion(dpy, g_xregion_tmp, rects, nrects); 2106 | add_damage(dpy, g_xregion_tmp); 2107 | } 2108 | 2109 | #if DEBUG_EVENTS 2110 | static int 2111 | ev_serial(XEvent *ev) { 2112 | if ((ev->type & 0x7f) != KeymapNotify) { 2113 | return ev->xany.serial; 2114 | } 2115 | return NextRequest(ev->xany.display); 2116 | } 2117 | 2118 | static char * 2119 | ev_name(XEvent *ev) { 2120 | static char buf[128]; 2121 | // what?? switch (ev->type & 0x7f) { 2122 | switch (ev->type) { 2123 | case FocusIn: 2124 | return "FocusIn"; 2125 | case FocusOut: 2126 | return "FocusOut"; 2127 | case CreateNotify: 2128 | return "CreateNotify"; 2129 | case ConfigureNotify: 2130 | return "ConfigureNotify"; 2131 | case DestroyNotify: 2132 | return "DestroyNotify"; 2133 | case Expose: 2134 | return "Expose"; 2135 | case MapNotify: 2136 | return "Map"; 2137 | case UnmapNotify: 2138 | return "Unmap"; 2139 | case ReparentNotify: 2140 | return "Reparent"; 2141 | case CirculateNotify: 2142 | return "Circulate"; 2143 | case PropertyNotify: 2144 | return "PropertyNotify"; 2145 | default: 2146 | if (ev->type == damage_event + XDamageNotify) { 2147 | return "Damage"; 2148 | } 2149 | sprintf(buf, "Event %d", ev->type); 2150 | return buf; 2151 | } 2152 | } 2153 | 2154 | static Window 2155 | ev_window(XEvent *ev) { 2156 | switch (ev->type) { 2157 | case Expose: 2158 | return ev->xexpose.window; 2159 | case MapNotify: 2160 | return ev->xmap.window; 2161 | case UnmapNotify: 2162 | return ev->xunmap.window; 2163 | case ReparentNotify: 2164 | return ev->xreparent.window; 2165 | case CirculateNotify: 2166 | return ev->xcirculate.window; 2167 | default: 2168 | if (ev->type == damage_event + XDamageNotify) { 2169 | return ((XDamageNotifyEvent *)ev)->drawable; 2170 | } 2171 | return 0; 2172 | } 2173 | } 2174 | #endif 2175 | 2176 | void 2177 | usage(char *program, int exitcode) { 2178 | fprintf(stderr, "%s v0.5\n", program); 2179 | fprintf(stderr, "usage: %s [options]\n", program); 2180 | 2181 | fprintf(stderr, 2182 | R"SOMERANDOMTEXT(Options 2183 | -d display 2184 | Which display should be managed 2185 | -r radius 2186 | The blur radius for shadows. (default 12) 2187 | -o opacity 2188 | The translucency for shadows. (default .75) 2189 | -l left-offset 2190 | The left offset for shadows. (default -15) 2191 | -t top-offset 2192 | The top offset for shadows. (default -15) 2193 | -I fade-in-step 2194 | Opacity change between steps while fading in. (default 0.028) 2195 | -O fade-out-step 2196 | Opacity change between steps while fading out. (default 0.03) 2197 | -D fade-delta-time 2198 | The time between steps in a fade in milliseconds. (default 10) 2199 | -m opacity 2200 | The opacity for menus. (default 1.0) 2201 | -c 2202 | Enabled client-side shadows on windows. 2203 | -C 2204 | Avoid drawing shadows on dock/panel windows. 2205 | -f 2206 | Fade windows in/out when opening/closing. 2207 | -F 2208 | Fade windows during opacity changes. 2209 | -i opacity 2210 | Opacity of inactive windows. (0.1 - 1.0) 2211 | -e opacity 2212 | Opacity of window titlebars and borders. (0.1 - 1.0) 2213 | -S 2214 | Enable synchronous operation (for debugging). 2215 | --shadow-red value 2216 | Red color value of shadow (0.0 - 1.0, defaults to 0). 2217 | --shadow-green value 2218 | Green color value of shadow (0.0 - 1.0, defaults to 0). 2219 | --shadow-blue value 2220 | Blue color value of shadow (0.0 - 1.0, defaults to 0).)SOMERANDOMTEXT" 2221 | ); 2222 | fprintf(stderr, "\n"); 2223 | 2224 | exit(exitcode); 2225 | } 2226 | 2227 | static Bool 2228 | register_cm (Display *dpy) 2229 | { 2230 | Window w; 2231 | Atom a; 2232 | static char net_wm_cm[] = "_NET_WM_CM_Sxx"; 2233 | 2234 | snprintf (net_wm_cm, sizeof (net_wm_cm), "_NET_WM_CM_S%d", g_screen); 2235 | a = XInternAtom (dpy, net_wm_cm, False); 2236 | w = XGetSelectionOwner (dpy, a); 2237 | if (w != None) { 2238 | XTextProperty tp; 2239 | char **strs; 2240 | int count; 2241 | Atom winNameAtom = XInternAtom (dpy, "_NET_WM_NAME", False); 2242 | 2243 | if (!XGetTextProperty (dpy, w, &tp, winNameAtom) && 2244 | !XGetTextProperty (dpy, w, &tp, XA_WM_NAME)) 2245 | { 2246 | fprintf (stderr, 2247 | "Another composite manager is already running (0x%lx)\n", 2248 | (unsigned long) w); 2249 | return False; 2250 | } 2251 | if (XmbTextPropertyToTextList (dpy, &tp, &strs, &count) == Success) 2252 | { 2253 | fprintf (stderr, 2254 | "Another composite manager is already running (%s)\n", strs[0]); 2255 | XFreeStringList (strs); 2256 | } 2257 | XFree (tp.value); 2258 | return False; 2259 | } 2260 | 2261 | w = XCreateSimpleWindow (dpy, RootWindow (dpy, g_screen), 0, 0, 1, 1, 0, None, 2262 | None); 2263 | 2264 | Xutf8SetWMProperties (dpy, w, "fastcompmgr", "fastcompmgr", NULL, 0, NULL, NULL, 2265 | NULL); 2266 | 2267 | XSetSelectionOwner (dpy, a, w, 0); 2268 | return True; 2269 | } 2270 | 2271 | static void run_configures(Display *dpy){ 2272 | win *w; 2273 | for (w = list; w; w = w->next) { 2274 | if (w->need_configure && !w->destroyed){ 2275 | do_configure_win(dpy, w); 2276 | } 2277 | } 2278 | } 2279 | 2280 | static void 2281 | do_paint(Display *dpy){ 2282 | paint_all(dpy, all_damage); 2283 | XSync(dpy, False); 2284 | all_damage_is_dirty = False; 2285 | clip_changed = False; 2286 | } 2287 | 2288 | static Bool configure_timer_started = False; 2289 | static int configure_time = 0; 2290 | 2291 | /// When a window is moved, or resized, a lot of ConfigureNotify events 2292 | /// occur. However, painting and Xsyncing of complex windows, e.g. 2293 | /// web-browser contents, may introduce a considerable lag. Therefore, for each 2294 | /// window, we cache the "latest" configure event and paint the events after 2295 | /// some timeout. On the other hand, we want to handle other events, especially 2296 | /// damage events, as fast as possible, so we do not timeout in this case. 2297 | static void 2298 | check_paint(Display *dpy){ 2299 | if(unlikely(g_configure_needed)){ 2300 | const int EVERY_MILISEC = 2; 2301 | if(!configure_timer_started){ 2302 | // Not strictly necessary to paint now, but until we run, the 2303 | // configured window has already been moving/resizing for a (short) 2304 | // while, so give early feedback to the user. 2305 | run_configures(dpy); 2306 | do_paint(dpy); 2307 | configure_timer_started = True; 2308 | configure_time = get_time_in_milliseconds() + EVERY_MILISEC; 2309 | } else { 2310 | int delta; 2311 | delta = get_time_in_milliseconds() - configure_time; 2312 | if (delta < EVERY_MILISEC){ 2313 | return; 2314 | } 2315 | g_configure_needed = False; 2316 | configure_timer_started = False; 2317 | run_configures(dpy); 2318 | do_paint(dpy); 2319 | } 2320 | } else { 2321 | if(likely(all_damage_is_dirty)) { 2322 | do_paint(dpy); 2323 | } 2324 | } 2325 | } 2326 | 2327 | 2328 | int 2329 | main(int argc, char **argv) { 2330 | const static struct option longopt[] = { 2331 | { "shadow-red", required_argument, NULL, 0 }, 2332 | { "shadow-green", required_argument, NULL, 0 }, 2333 | { "shadow-blue", required_argument, NULL, 0 }, 2334 | { "help", no_argument, NULL, 0 }, 2335 | { 0, 0, 0, 0 }, 2336 | }; 2337 | 2338 | XEvent ev; 2339 | Window root_return, parent_return; 2340 | Window *children; 2341 | unsigned int nchildren; 2342 | int i; 2343 | XRectangle *expose_rects = 0; 2344 | int size_expose = 0; 2345 | int n_expose = 0; 2346 | struct pollfd ufd; 2347 | int p; 2348 | int composite_major, composite_minor; 2349 | double shadow_red = 0.0; 2350 | double shadow_green = 0.0; 2351 | double shadow_blue = 0.0; 2352 | char *display = 0; 2353 | int o; 2354 | int longopt_idx; 2355 | Bool no_dock_shadow = False; 2356 | bufferInit(ignore_ringbuf, 2048, ulong); 2357 | 2358 | for (i = 0; i < NUM_WINTYPES; ++i) { 2359 | win_type_fade[i] = False; 2360 | win_type_shadow[i] = False; 2361 | win_type_opacity[i] = 1.0; 2362 | } 2363 | 2364 | while ((o = getopt_long(argc, argv, "D:I:O:d:r:o:m:l:t:i:e:schnfFCaS", 2365 | longopt, &longopt_idx)) != -1) { 2366 | switch (o) { 2367 | // Long options 2368 | case 0: 2369 | switch (longopt_idx) { 2370 | case 0: shadow_red = normalize_d(atof(optarg)); break; 2371 | case 1: shadow_green = normalize_d(atof(optarg)); break; 2372 | case 2: shadow_blue = normalize_d(atof(optarg)); break; 2373 | case 3: usage(argv[0], 0); break; 2374 | default: 2375 | fprintf(stderr, "Bug, unhandeled longopt_idx %d\n", longopt_idx); 2376 | exit(2); 2377 | } 2378 | break; 2379 | // Short options 2380 | case 'd': 2381 | display = optarg; 2382 | break; 2383 | case 'D': 2384 | fade_delta = atoi(optarg); 2385 | if (fade_delta < 1) { 2386 | fade_delta = 10; 2387 | } 2388 | break; 2389 | case 'I': 2390 | fade_in_step = atof(optarg); 2391 | if (fade_in_step <= 0) { 2392 | fade_in_step = 0.01; 2393 | } 2394 | break; 2395 | case 'O': 2396 | fade_out_step = atof(optarg); 2397 | if (fade_out_step <= 0) { 2398 | fade_out_step = 0.01; 2399 | } 2400 | break; 2401 | case 'c': 2402 | for (i = 1; i < NUM_WINTYPES; ++i) { 2403 | win_type_shadow[i] = True; 2404 | } 2405 | win_type_shadow[WINTYPE_DESKTOP] = False; 2406 | break; 2407 | case 'h': usage(argv[0], 0); break; 2408 | case 'C': 2409 | no_dock_shadow = True; 2410 | break; 2411 | case 'm': 2412 | win_type_opacity[WINTYPE_DROPDOWN_MENU] = atof(optarg); 2413 | win_type_opacity[WINTYPE_POPUP_MENU] = atof(optarg); 2414 | break; 2415 | case 'f': 2416 | for (i = 1; i < NUM_WINTYPES; ++i) { 2417 | win_type_fade[i] = True; 2418 | } 2419 | break; 2420 | case 'F': 2421 | fade_trans = True; 2422 | break; 2423 | case 'S': 2424 | synchronize = True; 2425 | break; 2426 | case 'r': 2427 | shadow_radius = atoi(optarg); 2428 | break; 2429 | case 'o': 2430 | shadow_opacity = atof(optarg); 2431 | break; 2432 | case 'l': 2433 | shadow_offset_x = atoi(optarg); 2434 | break; 2435 | case 't': 2436 | shadow_offset_y = atoi(optarg); 2437 | break; 2438 | case 'i': 2439 | inactive_opacity = (double)atof(optarg); 2440 | break; 2441 | case 'e': 2442 | frame_opacity = (double)atof(optarg); 2443 | break; 2444 | case 'n': 2445 | case 'a': 2446 | case 's': 2447 | fprintf(stderr, "Warning: " 2448 | "-n, -a, and -s have been removed.\n"); 2449 | break; 2450 | default: 2451 | usage(argv[0], 1); 2452 | break; 2453 | } 2454 | } 2455 | 2456 | if (no_dock_shadow) { 2457 | win_type_shadow[WINTYPE_DOCK] = False; 2458 | } 2459 | 2460 | dpy = XOpenDisplay(display); 2461 | if (!dpy) { 2462 | fprintf(stderr, "Can't open display\n"); 2463 | exit(1); 2464 | } 2465 | g_dpy = dpy; 2466 | 2467 | XSetErrorHandler(error); 2468 | if (synchronize) { 2469 | XSynchronize(dpy, 1); 2470 | } 2471 | 2472 | g_screen = DefaultScreen(dpy); 2473 | root = RootWindow(dpy, g_screen); 2474 | 2475 | if (!XRenderQueryExtension(dpy, &render_event, &render_error)) { 2476 | fprintf(stderr, "No render extension\n"); 2477 | exit(1); 2478 | } 2479 | 2480 | if (!XQueryExtension(dpy, COMPOSITE_NAME, &composite_opcode, 2481 | &composite_event, &composite_error)) { 2482 | fprintf(stderr, "No composite extension\n"); 2483 | exit(1); 2484 | } 2485 | 2486 | XCompositeQueryVersion(dpy, &composite_major, &composite_minor); 2487 | 2488 | #if HAS_NAME_WINDOW_PIXMAP 2489 | if (composite_major > 0 || composite_minor >= 2) { 2490 | has_name_pixmap = True; 2491 | } 2492 | #endif 2493 | 2494 | if (!XDamageQueryExtension(dpy, &damage_event, &damage_error)) { 2495 | fprintf(stderr, "No damage extension\n"); 2496 | exit(1); 2497 | } 2498 | 2499 | if (!XFixesQueryExtension(dpy, &xfixes_event, &xfixes_error)) { 2500 | fprintf(stderr, "No XFixes extension\n"); 2501 | exit(1); 2502 | } 2503 | 2504 | if(! register_cm(dpy)) 2505 | exit(1); 2506 | 2507 | /* get atoms */ 2508 | atom_opacity = XInternAtom(dpy, 2509 | "_NET_WM_WINDOW_OPACITY", False); 2510 | atom_win_type = XInternAtom(dpy, 2511 | "_NET_WM_WINDOW_TYPE", False); 2512 | atom_pixmap = XInternAtom(dpy, 2513 | "PIXMAP", False); 2514 | atom_wm_state = XInternAtom(dpy, 2515 | "WM_STATE", False); 2516 | atom_net_frame_extents = XInternAtom(dpy, 2517 | "_NET_FRAME_EXTENTS", False); 2518 | atom_gtk_frame_extents = XInternAtom(dpy, 2519 | "_GTK_FRAME_EXTENTS", False); 2520 | win_type[WINTYPE_DESKTOP] = XInternAtom(dpy, 2521 | "_NET_WM_WINDOW_TYPE_DESKTOP", False); 2522 | win_type[WINTYPE_DOCK] = XInternAtom(dpy, 2523 | "_NET_WM_WINDOW_TYPE_DOCK", False); 2524 | win_type[WINTYPE_TOOLBAR] = XInternAtom(dpy, 2525 | "_NET_WM_WINDOW_TYPE_TOOLBAR", False); 2526 | win_type[WINTYPE_MENU] = XInternAtom(dpy, 2527 | "_NET_WM_WINDOW_TYPE_MENU", False); 2528 | win_type[WINTYPE_UTILITY] = XInternAtom(dpy, 2529 | "_NET_WM_WINDOW_TYPE_UTILITY", False); 2530 | win_type[WINTYPE_SPLASH] = XInternAtom(dpy, 2531 | "_NET_WM_WINDOW_TYPE_SPLASH", False); 2532 | win_type[WINTYPE_DIALOG] = XInternAtom(dpy, 2533 | "_NET_WM_WINDOW_TYPE_DIALOG", False); 2534 | win_type[WINTYPE_NORMAL] = XInternAtom(dpy, 2535 | "_NET_WM_WINDOW_TYPE_NORMAL", False); 2536 | win_type[WINTYPE_DROPDOWN_MENU] = XInternAtom(dpy, 2537 | "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False); 2538 | win_type[WINTYPE_POPUP_MENU] = XInternAtom(dpy, 2539 | "_NET_WM_WINDOW_TYPE_POPUP_MENU", False); 2540 | win_type[WINTYPE_TOOLTIP] = XInternAtom(dpy, 2541 | "_NET_WM_WINDOW_TYPE_TOOLTIP", False); 2542 | win_type[WINTYPE_NOTIFY] = XInternAtom(dpy, 2543 | "_NET_WM_WINDOW_TYPE_NOTIFICATION", False); 2544 | win_type[WINTYPE_COMBO] = XInternAtom(dpy, 2545 | "_NET_WM_WINDOW_TYPE_COMBO", False); 2546 | win_type[WINTYPE_DND] = XInternAtom(dpy, 2547 | "_NET_WM_WINDOW_TYPE_DND", False); 2548 | 2549 | gaussian_map = make_gaussian_map(dpy, shadow_radius); 2550 | presum_gaussian(gaussian_map); 2551 | 2552 | if(!root_init()){ 2553 | exit(1); 2554 | } 2555 | 2556 | black_picture = solid_picture(dpy, True, 1, 0, 0, 0); 2557 | 2558 | // Allow for user-defined shadow color: 2559 | if (!shadow_red && !shadow_green && !shadow_blue) 2560 | cshadow_picture = black_picture; 2561 | else 2562 | cshadow_picture = solid_picture(dpy, True, 1, 2563 | shadow_red, shadow_green, shadow_blue); 2564 | 2565 | all_damage = XFixesCreateRegion(dpy, 0, 0); 2566 | all_damage_is_dirty = False; 2567 | g_xregion_tmp = XFixesCreateRegion(dpy, 0, 0); 2568 | 2569 | clip_changed = True; 2570 | XGrabServer(dpy); 2571 | 2572 | XCompositeRedirectSubwindows( 2573 | dpy, root, CompositeRedirectManual); 2574 | 2575 | XSelectInput(dpy, root, 2576 | SubstructureNotifyMask 2577 | | ExposureMask 2578 | | StructureNotifyMask 2579 | | PropertyChangeMask); 2580 | 2581 | XQueryTree(dpy, root, &root_return, 2582 | &parent_return, &children, &nchildren); 2583 | 2584 | for (i = 0; i < nchildren; i++) { 2585 | add_win(dpy, children[i], i ? children[i-1] : None); 2586 | } 2587 | 2588 | XFree(children); 2589 | 2590 | XUngrabServer(dpy); 2591 | 2592 | ufd.fd = ConnectionNumber(dpy); 2593 | ufd.events = POLLIN; 2594 | 2595 | { 2596 | XRectangle root_rect = { .x=0, .y=0, 2597 | .width=root_width , .height=root_height }; 2598 | XFixesSetRegion(dpy, g_xregion_tmp, &root_rect, 1); 2599 | paint_all(dpy, g_xregion_tmp); 2600 | } 2601 | 2602 | for (;;) { 2603 | /* dump_wins(); */ 2604 | do { 2605 | if (!QLength(dpy)) { 2606 | // TODO: check and re-implement fade time logic. 2607 | int timeout = (configure_timer_started) ? 2 : fade_timeout(); 2608 | if (unlikely(poll(&ufd, 1, timeout) == 0)) { 2609 | check_paint(dpy); 2610 | // run_fades(dpy); 2611 | break; 2612 | } 2613 | } 2614 | 2615 | XNextEvent(dpy, &ev); 2616 | 2617 | if (likely((ev.type & 0x7f) != KeymapNotify)) { 2618 | discard_ignore(dpy, ev.xany.serial); 2619 | } 2620 | 2621 | #if DEBUG_EVENTS 2622 | fprintf(stderr, "event %s serial 0x%08x window 0x%08lx\n", 2623 | ev_name(&ev), ev_serial(&ev), ev_window(&ev)); 2624 | #endif 2625 | 2626 | switch (ev.type) { 2627 | case FocusIn: { 2628 | if (!inactive_opacity) break; 2629 | 2630 | // stop focusing windows the cursor is over. 2631 | // with this, windows dont focus right after being 2632 | // deiconified, this needs to be fixed by blocking 2633 | // the right kind of FocusOut event 2634 | if (ev.xfocus.detail == NotifyPointer) break; 2635 | 2636 | win *fw = find_win(dpy, ev.xfocus.window); 2637 | if (IS_NORMAL_WIN(fw)) { 2638 | set_opacity(dpy, fw, OPAQUE); 2639 | } 2640 | break; 2641 | } 2642 | case FocusOut: { 2643 | if (!inactive_opacity) break; 2644 | 2645 | // this fixes deiconify refocus 2646 | // need != notifygrab here otherwise windows wont 2647 | // lower opacity when grabbed for dragging 2648 | if (ev.xfocus.mode != NotifyGrab 2649 | && ev.xfocus.detail == NotifyVirtual) break; 2650 | 2651 | win *fw = find_win(dpy, ev.xfocus.window); 2652 | if (IS_NORMAL_WIN(fw)) { 2653 | set_opacity(dpy, fw, INACTIVE_OPACITY); 2654 | } 2655 | break; 2656 | } 2657 | case CreateNotify: 2658 | //if (ev.xcreatewindow.override_redirect) break; 2659 | add_win(dpy, ev.xcreatewindow.window, 0); 2660 | break; 2661 | case ConfigureNotify: 2662 | handle_ConfigureNotify(dpy, &ev.xconfigure); 2663 | break; 2664 | case DestroyNotify: 2665 | destroy_win(dpy, ev.xdestroywindow.window, True); 2666 | break; 2667 | case MapNotify: 2668 | map_win(dpy, ev.xmap.window, ev.xmap.serial, True); 2669 | break; 2670 | case UnmapNotify: 2671 | unmap_win(dpy, ev.xunmap.window, True); 2672 | break; 2673 | case ReparentNotify: 2674 | if (ev.xreparent.parent == root) { 2675 | add_win(dpy, ev.xreparent.window, 0); 2676 | } else { 2677 | destroy_win(dpy, ev.xreparent.window, True); 2678 | } 2679 | break; 2680 | case CirculateNotify: 2681 | circulate_win(dpy, &ev.xcirculate); 2682 | break; 2683 | case Expose: 2684 | if (ev.xexpose.window == root) { 2685 | int more = ev.xexpose.count + 1; 2686 | if (n_expose == size_expose) { 2687 | if (expose_rects) { 2688 | expose_rects = realloc(expose_rects, 2689 | (size_expose + more) * sizeof(XRectangle)); 2690 | size_expose += more; 2691 | } else { 2692 | expose_rects = malloc(more * sizeof(XRectangle)); 2693 | size_expose = more; 2694 | } 2695 | } 2696 | expose_rects[n_expose].x = ev.xexpose.x; 2697 | expose_rects[n_expose].y = ev.xexpose.y; 2698 | expose_rects[n_expose].width = ev.xexpose.width; 2699 | expose_rects[n_expose].height = ev.xexpose.height; 2700 | n_expose++; 2701 | if (ev.xexpose.count == 0) { 2702 | expose_root(dpy, root, expose_rects, n_expose); 2703 | n_expose = 0; 2704 | } 2705 | } 2706 | break; 2707 | case PropertyNotify: 2708 | for (p = 0; root_background_props[p]; p++) { 2709 | if (ev.xproperty.atom == 2710 | XInternAtom(dpy, root_background_props[p], False)) { 2711 | if (root_tile) { 2712 | XClearArea(dpy, root, 0, 0, 0, 0, True); 2713 | XRenderFreePicture(dpy, root_tile); 2714 | root_tile = None; 2715 | break; 2716 | } 2717 | } 2718 | } 2719 | /* check if Trans property was changed */ 2720 | if (ev.xproperty.atom == atom_opacity) { 2721 | /* reset mode and redraw window */ 2722 | win *w = find_win(dpy, ev.xproperty.window); 2723 | if (w) { 2724 | double def = win_type_opacity[w->window_type]; 2725 | set_opacity(dpy, w, 2726 | get_opacity_prop(dpy, w, (unsigned long)(OPAQUE * def))); 2727 | } 2728 | } 2729 | break; 2730 | case SelectionClear: 2731 | fprintf(stderr, "Another composite manager started and took the _NET_WM_CM_Sn " 2732 | "selection. Bye.\n"); 2733 | exit(0); 2734 | break; 2735 | default: 2736 | if (likely(ev.type == damage_event + XDamageNotify)) { 2737 | damage_win(dpy, (XDamageNotifyEvent *)&ev); 2738 | } 2739 | break; 2740 | } 2741 | } while (QLength(dpy)); 2742 | 2743 | check_paint(dpy); 2744 | } 2745 | } 2746 | -------------------------------------------------------------------------------- /ringbuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013 Philip Thrasher 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | * this software and associated documentation files (the "Software"), to deal in 8 | * the Software without restriction, including without limitation the rights to 9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | * the Software, and to permit persons to whom the Software is furnished to do so, 11 | * subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | * 23 | * 24 | * 25 | * Philip Thrasher's Crazy Awesome Ring Buffer Macros! 26 | * 27 | * Below you will find some naughty macros for easy owning and manipulating 28 | * generic ring buffers. Yes, they are slightly evil in readability, but they 29 | * are really fast, and they work great. 30 | * 31 | * Example usage: 32 | * 33 | * #include 34 | * 35 | * // So we can use this in any method, this gives us a typedef 36 | * // named 'intBuffer'. 37 | * ringBuffer_typedef(int, intBuffer); 38 | * 39 | * int main() { 40 | * // Declare vars. 41 | * intBuffer myBuffer; 42 | * 43 | * bufferInit(myBuffer,1024,int); 44 | * 45 | * // We must have the pointer. All of the macros deal with the pointer. 46 | * // (except for init.) 47 | * intBuffer* myBuffer_ptr; 48 | * myBuffer_ptr = &myBuffer; 49 | * 50 | * // Write two values. 51 | * bufferWrite(myBuffer_ptr,37); 52 | * bufferWrite(myBuffer_ptr,72); 53 | * 54 | * // Read a value into a local variable. 55 | * int first; 56 | * bufferRead(myBuffer_ptr,first); 57 | * assert(first == 37); // true 58 | * 59 | * // Get reference of the current element to avoid copies 60 | * int* second_ptr = &bufferReadPeek(myBuffer_ptr); 61 | * assert(*second_ptr == 72); // true 62 | * operate_on_reference(second_prt); 63 | * 64 | * // move to next value 65 | * bufferReadSkip(myBuffer_ptr); 66 | * 67 | * return 0; 68 | * } 69 | * 70 | */ 71 | 72 | #ifndef _ringbuffer_h 73 | #define _ringbuffer_h 74 | 75 | #include 76 | 77 | /* Setting RINGBUFFER_USE_STATIC_MEMORY to 1 will use a static memory area 78 | * allocated for the ringbuffer instead of a dynamic one, useful if there is no 79 | * dynamic allocation or the buffer will always be around anyways. There is no 80 | * bufferDestroy() in this case */ 81 | #ifndef RINGBUFFER_USE_STATIC_MEMORY 82 | #define RINGBUFFER_USE_STATIC_MEMORY 0 83 | #endif 84 | 85 | /* Setting RINGBUFFER_AVOID_MODULO to 1 will replace the modulo operations with 86 | * comparisons, useful if there is no hardware divide operations */ 87 | #ifndef RINGBUFFER_AVOID_MODULO 88 | #define RINGBUFFER_AVOID_MODULO 0 89 | #endif 90 | 91 | #define ringBuffer_typedef(T, NAME) \ 92 | typedef struct { \ 93 | int size; \ 94 | int start; \ 95 | int end; \ 96 | T* elems; \ 97 | } NAME 98 | 99 | 100 | /* To allow for S elements to be stored we allocate space for (S + 1) elements, 101 | * otherwise a full buffer would be indistinguishable from an empty buffer. 102 | * 103 | * Also as the elements will be accessed like an array it makes sense to pad 104 | * the type of the elements to the platform wordsize, otherwise you will end up 105 | * with unaligned accesses. */ 106 | #if RINGBUFFER_USE_STATIC_MEMORY == 1 107 | #define bufferInit(BUF, S, T) \ 108 | { \ 109 | static T StaticBufMemory[S + 1];\ 110 | BUF.elems = StaticBufMemory; \ 111 | } \ 112 | BUF.size = S; \ 113 | BUF.start = 0; \ 114 | BUF.end = 0; 115 | #else 116 | 117 | #define bufferInit(BUF, S, T) \ 118 | do { \ 119 | (BUF).size = (S); \ 120 | (BUF).start = 0; \ 121 | (BUF).end = 0; \ 122 | (BUF).elems = (T*)calloc((BUF).size + 1, sizeof(T)); \ 123 | } while(0) 124 | 125 | 126 | /* Increase the buffer size of dynamic arrays. */ 127 | #define bufferIncrease(BUF, BUFSIZE) \ 128 | do { \ 129 | const size_t sizeof_t = sizeof( typeof(*(BUF)->elems)); \ 130 | typeof((BUF)->elems) ELEMS = (typeof((BUF)->elems))calloc(BUFSIZE+1,sizeof_t); \ 131 | int NEW_END; \ 132 | if(isBufferEmpty(BUF)){ \ 133 | NEW_END = 0; \ 134 | } else { \ 135 | if((BUF)->start < (BUF)->end ){ \ 136 | NEW_END = (BUF)->end - (BUF)->start; \ 137 | memcpy(ELEMS, &(BUF)->elems[(BUF)->start], NEW_END*sizeof_t); \ 138 | } else { \ 139 | int COUNT1 = ((BUF)->size + 1 - (BUF)->start); \ 140 | int COUNT2 = (BUF)->end; \ 141 | NEW_END = COUNT1 + COUNT2; \ 142 | memcpy(ELEMS, &(BUF)->elems[(BUF)->start], COUNT1*sizeof_t); \ 143 | memcpy(&ELEMS[COUNT1], (BUF)->elems, COUNT2*sizeof_t); \ 144 | } \ 145 | } \ 146 | free((BUF)->elems); \ 147 | (BUF)->start = 0; \ 148 | (BUF)->end = NEW_END; \ 149 | (BUF)->elems = (ELEMS); \ 150 | (BUF)->size = (BUFSIZE); \ 151 | } while(0) 152 | 153 | 154 | #define bufferDestroy(BUF) \ 155 | do { \ 156 | free((BUF)->elems); \ 157 | } while(0) 158 | 159 | #endif 160 | 161 | 162 | #if RINGBUFFER_AVOID_MODULO == 1 163 | 164 | #define nextStartIndex(BUF) (((BUF)->start != (BUF)->size) ? ((BUF)->start + 1) : 0) 165 | #define nextEndIndex(BUF) (((BUF)->end != (BUF)->size) ? ((BUF)->end + 1) : 0) 166 | 167 | #else 168 | 169 | #define nextStartIndex(BUF) (((BUF)->start + 1) % ((BUF)->size + 1)) 170 | #define nextEndIndex(BUF) (((BUF)->end + 1) % ((BUF)->size + 1)) 171 | 172 | #endif 173 | 174 | #define isBufferEmpty(BUF) ((BUF)->end == (BUF)->start) 175 | #define isBufferFull(BUF) (nextEndIndex(BUF) == (BUF)->start) 176 | 177 | #define bufferWritePeek(BUF) (BUF)->elems[(BUF)->end] 178 | #define bufferWriteSkip(BUF) \ 179 | do { \ 180 | (BUF)->end = nextEndIndex(BUF); \ 181 | if (isBufferEmpty(BUF)) { \ 182 | (BUF)->start = nextStartIndex(BUF); \ 183 | } \ 184 | } while(0) 185 | 186 | #define bufferReadPeek(BUF) (BUF)->elems[(BUF)->start] 187 | #define bufferReadSkip(BUF) \ 188 | do { \ 189 | (BUF)->start = nextStartIndex(BUF); \ 190 | } while(0) 191 | 192 | #define bufferWrite(BUF, ELEM) \ 193 | do { \ 194 | bufferWritePeek(BUF) = ELEM; \ 195 | bufferWriteSkip(BUF); \ 196 | } while(0) 197 | 198 | #define bufferRead(BUF, ELEM) \ 199 | do { \ 200 | ELEM = bufferReadPeek(BUF); \ 201 | bufferReadSkip(BUF); \ 202 | } while(0) 203 | 204 | 205 | #endif 206 | --------------------------------------------------------------------------------