├── .github └── issue_template.md ├── .gitignore ├── CPackConfig.cmake ├── Doxyfile ├── LICENSE ├── Makefile ├── README.md ├── _CMakeLists.txt ├── bin ├── compton-convgen.py └── compton-trans ├── compton-default-fshader-win.glsl ├── compton-fake-transparency-fshader-win.glsl ├── compton.desktop ├── compton.sample.conf ├── dbus-examples ├── cdbus-driver.sh └── inverter.sh ├── desc.txt ├── functions.sh ├── make-release.sh ├── man ├── CMakeLists.txt ├── compton-trans.1.asciidoc └── compton.1.asciidoc ├── media ├── compton.svg └── icons │ └── 48x48 │ └── compton.png ├── src ├── c2.c ├── c2.h ├── common.h ├── compton.c ├── compton.h ├── dbus.c ├── dbus.h ├── opengl.c ├── opengl.h ├── xrescheck.c └── xrescheck.h └── tests ├── cmake-test.sh └── make-tests.sh /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Platform: 4 | 5 | GPU, drivers, and screen setup: 6 | 7 | Compton version: 8 | 9 | Compton configuration: 10 | 11 | ### Steps of reproduction 12 | 13 | 14 | 15 | ### Expected behavior 16 | 17 | 18 | 19 | ### Current Behavior & Other details 20 | 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build files 2 | .deps 3 | aclocal.m4 4 | autom4te.cache 5 | config.h 6 | config.h.in 7 | config.log 8 | config.status 9 | configure 10 | depcomp 11 | install-sh 12 | missing 13 | stamp-h1 14 | compton 15 | *.o 16 | build/ 17 | 18 | # CMake files 19 | compton-*.deb 20 | compton-*.rpm 21 | compton-*.tar.bz2 22 | release/ 23 | _CPack_Packages/ 24 | CMakeCache.txt 25 | CMakeFiles/ 26 | cmake_install.cmake 27 | CPackSourceConfig.cmake 28 | install_manifest.txt 29 | 30 | 31 | # Vim files 32 | .sw[a-z] 33 | .*.sw[a-z] 34 | *~ 35 | 36 | # Misc files 37 | /core 38 | /*.core 39 | oprofile_data/ 40 | compton.plist 41 | callgrind.out.* 42 | man/*.html 43 | man/*.1 44 | doxygen/ 45 | .clang_complete 46 | .ycm_extra_conf.py 47 | .ycm_extra_conf.pyc 48 | /src/backtrace-symbols.[ch] 49 | /compton*.trace 50 | -------------------------------------------------------------------------------- /CPackConfig.cmake: -------------------------------------------------------------------------------- 1 | # == Environment == 2 | if (NOT CPACK_SYSTEM_NAME) 3 | set(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_PROCESSOR}") 4 | if (CPACK_SYSTEM_NAME STREQUAL "x86_64") 5 | set(CPACK_SYSTEM_NAME "amd64") 6 | endif () 7 | endif () 8 | 9 | # == Basic information == 10 | set(CPACK_PACKAGE_NAME "compton") 11 | set(CPACK_PACKAGE_VENDOR "chjj") 12 | set(CPACK_PACKAGE_VERSION_MAJOR "0") 13 | set(CPACK_PACKAGE_VERSION_MINOR "0") 14 | set(CPACK_PACKAGE_VERSION_PATCH "0") 15 | set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") 16 | set(CPACK_PACKAGE_DESCRIPTION "A lightweight X compositing window manager, fork of xcompmgr-dana.") 17 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A lightweight X compositing window manager") 18 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_SYSTEM_NAME}") 19 | set(CPACK_PACKAGE_CONTACT "nobody ") 20 | set(CPACK_INSTALL_COMMANDS "env PREFIX=build make install") 21 | 22 | # == Package config == 23 | set(CPACK_INSTALLED_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/build" "usr") 24 | set(CPACK_GENERATOR "TBZ2" "DEB" "RPM") 25 | set(CPACK_RESOURCE_FILE_LICENSE "LICENSE") 26 | set(CPACK_RESOURCE_FILE_README "README.md") 27 | set(CPACK_STRIP_FILES 1) 28 | 29 | # == DEB package config == 30 | set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "${CPACK_SYSTEM_NAME}") 31 | set(CPACK_DEBIAN_PACKAGE_SECTION "x11") 32 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.15), libconfig9, libdbus-1-3 (>= 1.1.1), libgl1-mesa-glx | libgl1 | libgl1-nvidia-glx | libgl1-fglrx-glx, libpcre3 (>= 8.10), libx11-6, libxcomposite1 (>= 1:0.3-1), libxdamage1 (>= 1:1.1), libxext6, libxfixes3, libxrandr2 (>= 4.3), libxrender1, libxinerama1") 33 | 34 | # == RPM package config == 35 | set(CPACK_RPM_PACKAGE_REQUIRES "/bin/sh,libGL.so.1,libX11.so.6,libXcomposite.so.1,libXdamage.so.1,libXext.so.6,libXfixes.so.3,libXrandr.so.2,libXrender.so.1,libc.so.6,libconfig.so.9,libdbus-1.so.3,libm.so.6,libpcre.so.1") 36 | 37 | # == Source package config == 38 | set(CPACK_SOURCE_GENERATOR "TBZ2 DEB RPM") 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | compton - a compositor for X11 2 | 3 | Contributors 4 | 5 | Based on xcompmgr, originally written by Keith Packard, with modifications 6 | from several contributors (according to the xcompmgr man page): Matthew Allum, 7 | Eric Anholt, Dan Doel, Thomas Luebking, Matthew Hawn, Ely Levy, Phil Blundell, 8 | and Carl Worth. Menu transparency was implemented by Dana Jansens. 9 | 10 | Numerous contributions to compton from Richard Grenville. 11 | 12 | xcompmgr 13 | 14 | Copyright © 2003 Keith Packard 15 | 16 | Permission to use, copy, modify, distribute, and sell this software and its 17 | documentation for any purpose is hereby granted without fee, provided that 18 | the above copyright notice appear in all copies and that both that 19 | copyright notice and this permission notice appear in supporting 20 | documentation, and that the name of Keith Packard not be used in 21 | advertising or publicity pertaining to distribution of the software without 22 | specific, written prior permission. Keith Packard makes no 23 | representations about the suitability of this software for any purpose. It 24 | is provided "as is" without express or implied warranty. 25 | 26 | KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 27 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 28 | EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR 29 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 30 | DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 31 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 32 | PERFORMANCE OF THIS SOFTWARE. 33 | 34 | compton 35 | 36 | Copyright (c) 2011, Christopher Jeffrey (github.com/chjj) 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy 39 | of this software and associated documentation files (the "Software"), to deal 40 | in the Software without restriction, including without limitation the rights 41 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 42 | copies of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in 46 | all copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 54 | THE SOFTWARE. 55 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Use tab to indent recipe lines, spaces to indent other lines, otherwise 2 | # GNU make may get unhappy. 3 | 4 | CC ?= gcc 5 | 6 | PREFIX ?= /usr 7 | BINDIR ?= $(PREFIX)/bin 8 | MANDIR ?= $(PREFIX)/share/man/man1 9 | APPDIR ?= $(PREFIX)/share/applications 10 | ICODIR ?= $(PREFIX)/share/icons/hicolor/ 11 | 12 | PACKAGES = x11 xcomposite xfixes xdamage xrender xext xrandr 13 | LIBS = -lm -lrt 14 | INCS = 15 | 16 | OBJS = compton.o 17 | 18 | # === Configuration flags === 19 | CFG = -std=c99 20 | 21 | # ==== Xinerama ==== 22 | # Enables support for --xinerama-shadow-crop 23 | ifeq "$(NO_XINERAMA)" "" 24 | CFG += -DCONFIG_XINERAMA 25 | PACKAGES += xinerama 26 | endif 27 | 28 | # ==== libconfig ==== 29 | # Enables configuration file parsing support 30 | ifeq "$(NO_LIBCONFIG)" "" 31 | CFG += -DCONFIG_LIBCONFIG 32 | PACKAGES += libconfig 33 | 34 | # libconfig-1.3* does not define LIBCONFIG_VER* macros, so we use 35 | # pkg-config to determine its version here 36 | CFG += $(shell pkg-config --atleast-version=1.4 libconfig || echo '-DCONFIG_LIBCONFIG_LEGACY') 37 | endif 38 | 39 | # ==== PCRE regular expression ==== 40 | # Enables support for PCRE regular expression pattern in window conditions 41 | ifeq "$(NO_REGEX_PCRE)" "" 42 | CFG += -DCONFIG_REGEX_PCRE 43 | LIBS += $(shell pcre-config --libs) 44 | INCS += $(shell pcre-config --cflags) 45 | # Enables JIT support in libpcre 46 | ifeq "$(NO_REGEX_PCRE_JIT)" "" 47 | CFG += -DCONFIG_REGEX_PCRE_JIT 48 | endif 49 | endif 50 | 51 | # ==== DRM VSync ==== 52 | # Enables support for "drm" VSync method 53 | ifeq "$(NO_VSYNC_DRM)" "" 54 | INCS += $(shell pkg-config --cflags libdrm) 55 | CFG += -DCONFIG_VSYNC_DRM 56 | endif 57 | 58 | # ==== OpenGL ==== 59 | # Enables support for GLX backend, OpenGL VSync methods, etc. 60 | ifeq "$(NO_VSYNC_OPENGL)" "" 61 | CFG += -DCONFIG_VSYNC_OPENGL 62 | # -lGL must precede some other libraries, or it segfaults on FreeBSD (#74) 63 | LIBS := -lGL $(LIBS) 64 | OBJS += opengl.o 65 | # Enables support for GLSL (GLX background blur, etc.) 66 | ifeq "$(NO_VSYNC_OPENGL_GLSL)" "" 67 | CFG += -DCONFIG_VSYNC_OPENGL_GLSL 68 | endif 69 | # Enables support for GL FBO (GLX multi-pass blur, etc.) 70 | ifeq "$(NO_VSYNC_OPENGL_FBO)" "" 71 | CFG += -DCONFIG_VSYNC_OPENGL_FBO 72 | endif 73 | # Enables support for GL VBO (does nothing right now) 74 | ifeq "$(NO_VSYNC_OPENGL_VBO)" "" 75 | CFG += -DCONFIG_VSYNC_OPENGL_VBO 76 | endif 77 | endif 78 | 79 | # ==== D-Bus ==== 80 | # Enables support for --dbus (D-Bus remote control) 81 | ifeq "$(NO_DBUS)" "" 82 | CFG += -DCONFIG_DBUS 83 | PACKAGES += dbus-1 84 | OBJS += dbus.o 85 | endif 86 | 87 | # ==== X Sync ==== 88 | # Enables support for --xrender-sync-fence 89 | ifeq "$(NO_XSYNC)" "" 90 | CFG += -DCONFIG_XSYNC 91 | endif 92 | 93 | # ==== C2 ==== 94 | # Enable window condition support 95 | ifeq "$(NO_C2)" "" 96 | CFG += -DCONFIG_C2 97 | OBJS += c2.o 98 | endif 99 | 100 | # ==== X resource checker ==== 101 | # Enable X resource leakage checking (Pixmap only, presently) 102 | ifneq "$(ENABLE_XRESCHECK)" "" 103 | CFG += -DDEBUG_XRC 104 | OBJS += xrescheck.o 105 | endif 106 | 107 | # === Version string === 108 | COMPTON_VERSION ?= git-$(shell git describe --always --dirty)-$(shell git log -1 --date=short --pretty=format:%cd) 109 | CFG += -DCOMPTON_VERSION="\"$(COMPTON_VERSION)\"" 110 | 111 | LDFLAGS ?= -Wl,-O1 -Wl,--as-needed 112 | 113 | ifeq "$(CFG_DEV)" "" 114 | CFLAGS ?= -DNDEBUG -O2 -D_FORTIFY_SOURCE=2 115 | else 116 | CC = clang 117 | export LD_ALTEXEC = /usr/bin/ld.gold 118 | OBJS += backtrace-symbols.o 119 | LIBS += -lbfd 120 | CFLAGS += -ggdb -Wshadow 121 | # CFLAGS += -Weverything -Wno-disabled-macro-expansion -Wno-padded -Wno-gnu 122 | endif 123 | 124 | LIBS += $(shell pkg-config --libs $(PACKAGES)) 125 | INCS += $(shell pkg-config --cflags $(PACKAGES)) 126 | 127 | CFLAGS += -Wall 128 | 129 | BINS = compton bin/compton-trans 130 | MANPAGES = man/compton.1 man/compton-trans.1 131 | MANPAGES_HTML = $(addsuffix .html,$(MANPAGES)) 132 | 133 | # === Recipes === 134 | .DEFAULT_GOAL := compton 135 | 136 | src/.clang_complete: Makefile 137 | @(for i in $(filter-out -O% -DNDEBUG, $(CFG) $(CPPFLAGS) $(CFLAGS) $(INCS)); do echo "$$i"; done) > $@ 138 | 139 | %.o: src/%.c src/%.h src/common.h 140 | $(CC) $(CFG) $(CPPFLAGS) $(CFLAGS) $(INCS) -c src/$*.c 141 | 142 | compton: $(OBJS) 143 | $(CC) $(CFG) $(CPPFLAGS) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJS) $(LIBS) 144 | 145 | man/%.1: man/%.1.asciidoc 146 | a2x --format manpage $< 147 | 148 | man/%.1.html: man/%.1.asciidoc 149 | asciidoc $< 150 | 151 | docs: $(MANPAGES) $(MANPAGES_HTML) 152 | 153 | install: $(BINS) docs 154 | @install -d "$(DESTDIR)$(BINDIR)" "$(DESTDIR)$(MANDIR)" "$(DESTDIR)$(APPDIR)" 155 | @install -m755 $(BINS) "$(DESTDIR)$(BINDIR)"/ 156 | ifneq "$(MANPAGES)" "" 157 | @install -m644 $(MANPAGES) "$(DESTDIR)$(MANDIR)"/ 158 | endif 159 | @install -d \ 160 | "$(DESTDIR)$(ICODIR)/scalable/apps" \ 161 | "$(DESTDIR)$(ICODIR)/48x48/apps" 162 | @install -m644 media/compton.svg "$(DESTDIR)$(ICODIR)/scalable/apps"/ 163 | @install -m644 media/icons/48x48/compton.png "$(DESTDIR)$(ICODIR)/48x48/apps"/ 164 | @install -m644 compton.desktop "$(DESTDIR)$(APPDIR)"/ 165 | ifneq "$(DOCDIR)" "" 166 | @install -d "$(DESTDIR)$(DOCDIR)" 167 | @install -m644 README.md compton.sample.conf "$(DESTDIR)$(DOCDIR)"/ 168 | @install -m755 dbus-examples/cdbus-driver.sh "$(DESTDIR)$(DOCDIR)"/ 169 | endif 170 | 171 | uninstall: 172 | @rm -f "$(DESTDIR)$(BINDIR)/compton" "$(DESTDIR)$(BINDIR)/compton-trans" 173 | @rm -f $(addprefix "$(DESTDIR)$(MANDIR)"/, compton.1 compton-trans.1) 174 | @rm -f "$(DESTDIR)$(APPDIR)/compton.desktop" 175 | ifneq "$(DOCDIR)" "" 176 | @rm -f $(addprefix "$(DESTDIR)$(DOCDIR)"/, README.md compton.sample.conf cdbus-driver.sh) 177 | endif 178 | 179 | clean: 180 | @rm -f $(OBJS) compton $(MANPAGES) $(MANPAGES_HTML) .clang_complete 181 | 182 | version: 183 | @echo "$(COMPTON_VERSION)" 184 | 185 | .PHONY: uninstall clean docs version 186 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a fork of [Compton](https://github.com/chjj/compton) that adds animated window transitions. It is intended for tiling window managers, but may be useful to people who use a mouse as well. 2 | 3 | Video: https://youtu.be/eKwPkiACqF0 4 | 5 | It adds the following options: 6 | * `transition-length` length of animation in milliseconds (default: 300) 7 | * `transition-pow-x` animation easing on the x-axis (default: 1.5) 8 | * `transition-pow-y` animation easing on the y-axis (default: 1.5) 9 | * `transition-pow-w` animation easing on the window width (default: 1.5) 10 | * `transition-pow-h` animation easing on the window height (default: 1.5) 11 | * `size-transition` whether to animate window size changes (default: true) 12 | * `spawn-center-screen` whether to animate new windows from the center of the screen (default: false) 13 | * `spawn-center` whether to animate new windows from their own center (default: true) 14 | * `no-scale-down` Whether to animate down scaling (some programs handle this poorly) (default: false) 15 | -------------------------------------------------------------------------------- /_CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(compton) 2 | cmake_minimum_required(VERSION 2.8) 3 | 4 | set(CPACK_PACKAGE_VERSION_MAJOR "0") 5 | set(CPACK_PACKAGE_VERSION_MINOR "0") 6 | set(CPACK_PACKAGE_VERSION_PATCH "0") 7 | 8 | add_subdirectory(man) 9 | 10 | set(compton_SRCS src/compton.c) 11 | 12 | set(CMAKE_C_FLAGS_DEBUG "-ggdb") 13 | set(CMAKE_C_FLAGS_RELEASE "-O2 -march=native") 14 | set(CMAKE_C_FLAGS_RELWITHDBGINFO "-O2 -march=native -ggdb") 15 | 16 | add_definitions("-Wall" "-std=c99") 17 | 18 | # == Version == 19 | execute_process(COMMAND sh -c "echo -n \\\"git-$(git describe --always --dirty)-$(git log -1 --date=short --pretty=format:%cd)\\\"" 20 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 21 | OUTPUT_VARIABLE COMPTON_VERSION) 22 | add_definitions("-DCOMPTON_VERSION=${COMPTON_VERSION}") 23 | 24 | # == Options == 25 | 26 | include(CMakeDependentOption) 27 | 28 | option(CONFIG_REGEX_PCRE "Enable PCRE regular expression support for blacklist entries (requires libpcre)" ON) 29 | if (CONFIG_REGEX_PCRE) 30 | add_definitions("-DCONFIG_REGEX_PCRE") 31 | endif () 32 | 33 | option(CONFIG_REGEX_PCRE_JIT "Use JIT support of libpcre)" ON) 34 | if (CONFIG_REGEX_PCRE_JIT) 35 | add_definitions("-DCONFIG_REGEX_PCRE_JIT") 36 | endif () 37 | 38 | option(CONFIG_LIBCONFIG "Enable configuration file parsing using libconfig" ON) 39 | if (CONFIG_LIBCONFIG) 40 | add_definitions("-DCONFIG_LIBCONFIG") 41 | endif () 42 | 43 | option(CONFIG_VSYNC_DRM "Enable DRM VSync support" ON) 44 | if (CONFIG_VSYNC_DRM) 45 | add_definitions("-DCONFIG_VSYNC_DRM") 46 | endif () 47 | 48 | option(CONFIG_VSYNC_OPENGL "Enable OpenGL support" ON) 49 | if (CONFIG_VSYNC_OPENGL) 50 | add_definitions("-DCONFIG_VSYNC_OPENGL") 51 | list(APPEND compton_SRCS src/opengl.c) 52 | endif () 53 | 54 | CMAKE_DEPENDENT_OPTION(CONFIG_VSYNC_OPENGL_GLSL 55 | "Enable GLSL support (GLX background blur, etc.)" ON 56 | "CONFIG_VSYNC_OPENGL" OFF) 57 | if (CONFIG_VSYNC_OPENGL_GLSL) 58 | add_definitions("-DCONFIG_VSYNC_OPENGL_GLSL") 59 | endif () 60 | 61 | CMAKE_DEPENDENT_OPTION(CONFIG_VSYNC_OPENGL_FBO 62 | "Enable OpenGL FBO support (GLX multi-pass blur, etc.)" ON 63 | "CONFIG_VSYNC_OPENGL" OFF) 64 | if (CONFIG_VSYNC_OPENGL_FBO) 65 | add_definitions("-DCONFIG_VSYNC_OPENGL_FBO") 66 | endif () 67 | 68 | CMAKE_DEPENDENT_OPTION(CONFIG_VSYNC_OPENGL_VBO 69 | "Enable OpenGL VBO support (does nothing right now)" ON 70 | "CONFIG_VSYNC_OPENGL" OFF) 71 | if (CONFIG_VSYNC_OPENGL_VBO) 72 | add_definitions("-DCONFIG_VSYNC_OPENGL_VBO") 73 | endif () 74 | 75 | option(CONFIG_XINERAMA "Enable additional Xinerama features" ON) 76 | if (CONFIG_XINERAMA) 77 | add_definitions("-DCONFIG_XINERAMA") 78 | endif () 79 | 80 | option(CONFIG_DBUS "Enable D-Bus support" ON) 81 | if (CONFIG_DBUS) 82 | add_definitions("-DCONFIG_DBUS") 83 | list(APPEND compton_SRCS src/dbus.c) 84 | endif () 85 | 86 | option(CONFIG_XSYNC "Enable X Sync support (X Sync fence)" ON) 87 | if (CONFIG_XSYNC) 88 | add_definitions("-DCONFIG_XSYNC") 89 | endif () 90 | 91 | option(CONFIG_C2 "Enable matching system" ON) 92 | if (CONFIG_C2) 93 | add_definitions("-DCONFIG_C2") 94 | list(APPEND compton_SRCS src/c2.c) 95 | endif () 96 | 97 | add_executable(compton ${compton_SRCS}) 98 | 99 | # == Find libraries == 100 | 101 | target_link_libraries(compton "-lm" "-lrt") 102 | 103 | if (CONFIG_VSYNC_OPENGL) 104 | target_link_libraries(compton "-lGL") 105 | endif () 106 | 107 | include(FindPkgConfig) 108 | 109 | # --- Find X11 libs --- 110 | set(X11_FIND_REQUIRED 1) 111 | include(FindX11) 112 | 113 | macro(X11LIB_CHK lib) 114 | if (NOT X11_${lib}_FOUND) 115 | message(FATAL_ERROR "Could not find lib${lib}.") 116 | endif () 117 | target_link_libraries(compton ${X11_${lib}_LIB}) 118 | endmacro () 119 | 120 | target_link_libraries(compton ${X11_X11_LIB}) 121 | X11LIB_CHK(Xcomposite) 122 | X11LIB_CHK(Xdamage) 123 | X11LIB_CHK(Xext) 124 | X11LIB_CHK(Xfixes) 125 | X11LIB_CHK(Xrender) 126 | X11LIB_CHK(Xrandr) 127 | if (CONFIG_XINERAMA) 128 | X11LIB_CHK(Xinerama) 129 | endif () 130 | 131 | # --- Find libpcre --- 132 | if (CONFIG_REGEX_PCRE) 133 | pkg_check_modules(LIBPCRE REQUIRED libpcre>=8.12) 134 | add_definitions(${LIBPCRE_CFLAGS}) 135 | target_link_libraries(compton ${LIBPCRE_LDFLAGS}) 136 | endif () 137 | 138 | # --- Find libconfig --- 139 | if (CONFIG_LIBCONFIG) 140 | pkg_check_modules(LIBCONFIG REQUIRED libconfig>=1.3.2) 141 | add_definitions(${LIBCONFIG_CFLAGS}) 142 | target_link_libraries(compton ${LIBCONFIG_LDFLAGS}) 143 | if (LIBCONFIG_VERSION VERSION_LESS 1.4) 144 | add_definitions(-DCONFIG_LIBCONFIG_LEGACY) 145 | message(STATUS "libconfig-1.3* detected. Enable legacy mode.") 146 | endif () 147 | endif () 148 | 149 | # --- Find libdbus --- 150 | if (CONFIG_DBUS) 151 | pkg_check_modules(DBUS REQUIRED dbus-1) 152 | add_definitions(${DBUS_CFLAGS}) 153 | target_link_libraries(compton ${DBUS_LDFLAGS}) 154 | endif () 155 | 156 | # --- Find libdrm --- 157 | if (CONFIG_VSYNC_DRM) 158 | pkg_check_modules(LIBDRM REQUIRED libdrm) 159 | # We only use its header file 160 | add_definitions(${LIBDRM_CFLAGS}) 161 | endif () 162 | 163 | # == Install == 164 | include(GNUInstallDirs) 165 | 166 | install(TARGETS compton 167 | DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT prog) 168 | install(FILES "${PROJECT_SOURCE_DIR}/bin/compton-trans" 169 | DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT prog) 170 | install(FILES 171 | "${PROJECT_SOURCE_DIR}/README.md" 172 | "${PROJECT_SOURCE_DIR}/LICENSE" 173 | DESTINATION "${CMAKE_INSTALL_DOCDIR}" COMPONENT doc) 174 | 175 | # == CPack == 176 | 177 | if (NOT CPACK_SYSTEM_NAME) 178 | set(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_PROCESSOR}") 179 | endif () 180 | 181 | set(CPACK_PACKAGE_DESCRIPTION "A lightweight X compositing window manager, fork of xcompmgr-dana.") 182 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A lightweight X compositing window manager") 183 | set(CPACK_PACKAGE_CONTACT "nobody ") 184 | set(CPACK_PACKAGE_DIRECTORY "${CMAKE_SOURCE_DIR}/release") 185 | set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/desc.txt") 186 | 187 | # --- Package config --- 188 | set(CPACK_GENERATOR "TBZ2" "DEB" "RPM") 189 | set(CPACK_MONOLITHIC_INSTALL 1) 190 | set(CPACK_STRIP_FILES 1) 191 | set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") 192 | set(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README.md") 193 | 194 | # --- Source package config --- 195 | set(CPACK_SOURCE_GENERATOR "TBZ2" "DEB" "RPM") 196 | set(CPACK_SOURCE_IGNORE_FILES 197 | "/\\\\." 198 | "\\\\.bak$" 199 | "\\\\.o$" 200 | "\\\\~$" 201 | "\\\\.plist$" 202 | "/compton$" 203 | # Generated package files 204 | "\\\\.tar\\\\.gz$" 205 | "\\\\.tar\\\\.bz2$" 206 | "\\\\.deb$" 207 | "\\\\.rpm$" 208 | "compton-.*\\\\.sh$" 209 | # CMake files 210 | "/Makefile$" 211 | "/CMakeFiles/" 212 | "\\\\.cmake$" 213 | "CMakeCache\\\\.txt$" 214 | "/build/" 215 | "/release/" 216 | "\\\\.diff$" 217 | "/oprofile_data/" 218 | "/install_manifest\\\\.txt$" 219 | ) 220 | 221 | # --- DEB package config --- 222 | set(CPACK_DEBIAN_PACKAGE_SECTION "x11") 223 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.15), libconfig9, libdbus-1-3 (>= 1.1.1), libgl1-mesa-glx | libgl1 | libgl1-nvidia-glx | libgl1-fglrx-glx, libpcre3 (>= 8.10), libx11-6, libxcomposite1 (>= 1:0.3-1), libxdamage1 (>= 1:1.1), libxext6, libxfixes3, libxrandr2 (>= 4.3), libxrender1, libxinerama1") 224 | 225 | # --- RPM package config --- 226 | set(CPACK_RPM_PACKAGE_LICENSE "unknown") 227 | set(CPACK_RPM_PACKAGE_REQUIRES "/bin/sh,libGL.so.1,libX11.so.6,libXcomposite.so.1,libXdamage.so.1,libXext.so.6,libXfixes.so.3,libXrandr.so.2,libXrender.so.1,libc.so.6,libconfig.so.9,libdbus-1.so.3,libm.so.6,libpcre.so.1") 228 | 229 | include(CPack) 230 | -------------------------------------------------------------------------------- /bin/compton-convgen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # vim:fileencoding=utf-8 4 | 5 | import math 6 | import argparse 7 | 8 | 9 | class CGError(Exception): 10 | '''An error in the convolution kernel generator.''' 11 | def __init__(self, desc): 12 | super().__init__(desc) 13 | 14 | 15 | class CGBadArg(CGError): 16 | '''An exception indicating an invalid argument has been passed to the 17 | convolution kernel generator.''' 18 | pass 19 | 20 | 21 | def mbuild(width, height): 22 | """Build a NxN matrix filled with 0.""" 23 | result = list() 24 | for i in range(height): 25 | result.append(list()) 26 | for j in range(width): 27 | result[i].append(0.0) 28 | return result 29 | 30 | 31 | def mdump(matrix): 32 | """Dump a matrix in natural format.""" 33 | for col in matrix: 34 | print("[ ", end='') 35 | for ele in col: 36 | print(format(ele, "13.6g") + ", ", end=" ") 37 | print("],") 38 | 39 | 40 | def mdumpcompton(matrix): 41 | """Dump a matrix in compton's format.""" 42 | width = len(matrix[0]) 43 | height = len(matrix) 44 | print("{},{},".format(width, height), end='') 45 | for i in range(height): 46 | for j in range(width): 47 | if int(height / 2) == i and int(width / 2) == j: 48 | continue 49 | print(format(matrix[i][j], ".6f"), end=",") 50 | print() 51 | 52 | 53 | def mnormalize(matrix): 54 | """Scale a matrix according to the value in the center.""" 55 | width = len(matrix[0]) 56 | height = len(matrix) 57 | factor = 1.0 / matrix[int(height / 2)][int(width / 2)] 58 | if 1.0 == factor: 59 | return matrix 60 | for i in range(height): 61 | for j in range(width): 62 | matrix[i][j] *= factor 63 | return matrix 64 | 65 | 66 | def mmirror4(matrix): 67 | """Do a 4-way mirroring on a matrix from top-left corner.""" 68 | width = len(matrix[0]) 69 | height = len(matrix) 70 | for i in range(height): 71 | for j in range(width): 72 | x = min(i, height - 1 - i) 73 | y = min(j, width - 1 - j) 74 | matrix[i][j] = matrix[x][y] 75 | return matrix 76 | 77 | 78 | def gen_gaussian(width, height, factors): 79 | """Build a Gaussian blur kernel.""" 80 | 81 | if width != height: 82 | raise CGBadArg("Cannot build an uneven Gaussian blur kernel.") 83 | 84 | size = width 85 | sigma = float(factors.get('sigma', 0.84089642)) 86 | 87 | result = mbuild(size, size) 88 | for i in range(int(size / 2) + 1): 89 | for j in range(int(size / 2) + 1): 90 | diffx = i - int(size / 2) 91 | diffy = j - int(size / 2) 92 | result[i][j] = 1.0 / (2 * math.pi * sigma) \ 93 | * pow(math.e, - (diffx * diffx + diffy * diffy) \ 94 | / (2 * sigma * sigma)) 95 | mnormalize(result) 96 | mmirror4(result) 97 | 98 | return result 99 | 100 | 101 | def gen_box(width, height, factors): 102 | """Build a box blur kernel.""" 103 | result = mbuild(width, height) 104 | for i in range(height): 105 | for j in range(width): 106 | result[i][j] = 1.0 107 | return result 108 | 109 | 110 | def _gen_invalid(width, height, factors): 111 | '''Handle a convolution kernel generation request of an unrecognized type.''' 112 | raise CGBadArg("Unknown kernel type.") 113 | 114 | 115 | def _args_readfactors(lst): 116 | """Parse the factor arguments.""" 117 | factors = dict() 118 | if lst: 119 | for s in lst: 120 | res = s.partition('=') 121 | if not res[0]: 122 | raise CGBadArg("Factor has no key.") 123 | if not res[2]: 124 | raise CGBadArg("Factor has no value.") 125 | factors[res[0]] = float(res[2]) 126 | return factors 127 | 128 | 129 | def _parse_args(): 130 | '''Parse the command-line arguments.''' 131 | 132 | parser = argparse.ArgumentParser(description='Build a convolution kernel.') 133 | parser.add_argument('type', help='Type of convolution kernel. May be "gaussian" (factor sigma = 0.84089642) or "box".') 134 | parser.add_argument('width', type=int, help='Width of convolution kernel. Must be an odd number.') 135 | parser.add_argument('height', nargs='?', type=int, help='Height of convolution kernel. Must be an odd number. Equals to width if omitted.') 136 | parser.add_argument('-f', '--factor', nargs='+', help='Factors of the convolution kernel, in name=value format.') 137 | parser.add_argument('--dump-compton', action='store_true', help='Dump in compton format.') 138 | return parser.parse_args() 139 | 140 | 141 | def _main(): 142 | args = _parse_args() 143 | 144 | width = args.width 145 | height = args.height 146 | if not height: 147 | height = width 148 | if not (width > 0 and height > 0): 149 | raise CGBadArg("Invalid width/height.") 150 | factors = _args_readfactors(args.factor) 151 | 152 | funcs = dict(gaussian=gen_gaussian, box=gen_box) 153 | matrix = (funcs.get(args.type, _gen_invalid))(width, height, factors) 154 | if args.dump_compton: 155 | mdumpcompton(matrix) 156 | else: 157 | mdump(matrix) 158 | 159 | 160 | if __name__ == '__main__': 161 | _main() 162 | -------------------------------------------------------------------------------- /bin/compton-trans: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # compton-trans 5 | # transset in a bash script 6 | # Copyright (c) 2011-2012, Christopher Jeffrey 7 | # 8 | 9 | # Usage: 10 | # $ compton-trans [options] [+|-]opacity 11 | # By window id 12 | # $ compton-trans -w "$WINDOWID" 75 13 | # By name 14 | # $ compton-trans -n "urxvt" 75 15 | # By current window 16 | # $ compton-trans -c 75 17 | # By selection 18 | # $ compton-trans 75 19 | # $ compton-trans -s 75 20 | # Increment current window 5% 21 | # $ compton-trans -c +5 22 | # Delete current window's opacity 23 | # $ compton-trans -c --delete 24 | # Reset all windows 25 | # $ compton-trans --reset 26 | 27 | # "command" is a shell built-in, faster than "which" 28 | if test -z "$(command -v xprop)" -o -z "$(command -v xwininfo)"; then 29 | echo 'The command xwininfo or xprop is not available. They might reside in a package named xwininfo, xprop, x11-utils, xorg-xprop, or xorg-xwininfo.' >& 2 30 | exit 1 31 | fi 32 | 33 | # Variables 34 | active= 35 | wprefix= 36 | window= 37 | opacity= 38 | cur= 39 | action= 40 | treeout= 41 | wid= 42 | topmost= 43 | lineno= 44 | option= 45 | v= 46 | 47 | # Workaround: replace '-5' with '~5' so as not to confuse getopts. 48 | for v in "$@"; do 49 | shift && set -- "$@" "$(echo "$v" | sed 's/^-\([0-9]\+%\?\)$/~\1/')" 50 | done 51 | 52 | # This takes into account the fact that getopts stops on 53 | # any argument it doesn't recongize or errors on. This 54 | # allows for things like `compton-trans -5` as well 55 | # as `compton-trans -c +5 -s` (contrived example). 56 | while test $# -gt 0; do 57 | # Reset option index 58 | OPTIND=1 59 | 60 | # Read options 61 | while getopts 'scrdgn:w:o:-:' option "$@"; do 62 | if test "$option" = '-'; then 63 | case "$OPTARG" in 64 | select | current | reset | delete | get) 65 | v='' 66 | ;; 67 | name | window | opacity) 68 | eval v=\$$OPTIND 69 | OPTIND=$((OPTIND + 1)) 70 | ;; 71 | name=* | window=* | opacity=*) 72 | v=$(echo "$OPTARG" | sed 's/^[^=]\+=//') 73 | ;; 74 | *) 75 | echo "$0: illegal option $OPTARG" >& 2 76 | exit 1 77 | ;; 78 | esac 79 | option=$(echo "$OPTARG" | cut -c 1) 80 | OPTARG=$v 81 | fi 82 | case "$option" in 83 | s) wprefix=''; window='' ;; 84 | c) 85 | active=$(xprop -root -notype _NET_ACTIVE_WINDOW \ 86 | | grep -Eo '0x[[:xdigit:]]+' | head -n 1) 87 | wprefix='-id'; window=$active 88 | ;; 89 | r) action='reset' ;; 90 | d) action='delete' ;; 91 | g) action='get' ;; 92 | n) wprefix='-name'; window=$OPTARG ;; 93 | w) wprefix='-id'; window=$OPTARG ;; 94 | o) opacity=$OPTARG ;; 95 | \?) exit 1 ;; 96 | esac 97 | done 98 | 99 | # Read positional arguments 100 | shift $((OPTIND - 1)) 101 | test -n "$1" && opacity=$1 && shift 102 | done 103 | 104 | # clean up opacity. xargs == a poor man's trim. 105 | opacity=$(echo "$opacity" | xargs | sed 's/%//g' | sed 's/^~\([0-9]\+\)$/-\1/') 106 | 107 | # Validate opacity value 108 | if test -z "$action" && ! echo "$opacity" | grep -q '^[+-]\?[0-9]\+$'; then 109 | echo "Invalid opacity specified: $opacity." 110 | exit 1 111 | fi 112 | 113 | # Reset opacity for all windows 114 | if test x"$action" = x'reset'; then 115 | xwininfo -root -tree \ 116 | | sed -n 's/^ \(0x[[:xdigit:]]*\).*/\1/p' \ 117 | | while IFS=$'\n' read wid; do 118 | xprop -id "$wid" -remove _NET_WM_WINDOW_OPACITY 119 | done 120 | exit 0 121 | fi 122 | 123 | # Get ID of the target window 124 | if test -z "$wprefix"; then 125 | treeout=$(xwininfo -children -frame) 126 | else 127 | test "$wprefix" = '-id' \ 128 | && ! echo "$window" | grep -Eiq '^[[:space:]]*(0x[[:xdigit:]]+|[[:digit:]]+)[[:space:]]*$' \ 129 | && echo 'Bad window ID.' && exit 1 130 | treeout=$(xwininfo -children $wprefix "$window") 131 | fi 132 | 133 | wid=$(echo "$treeout" | sed -n 's/^xwininfo:.*: \(0x[[:xdigit:]]*\).*$/\1/p') 134 | 135 | if test -z "$wid"; then 136 | echo 'Failed to find window.' 137 | exit 1 138 | fi 139 | 140 | # Make sure it's not root window 141 | if echo "$treeout" | fgrep -q 'Parent window id: 0x0'; then 142 | echo 'Cannot set opacity on root window.' 143 | exit 1 144 | fi 145 | 146 | # If it's already the topmost window 147 | if echo "$treeout" | grep -q 'Parent window id: 0x[[:xdigit:]]* (the root window)'; then 148 | topmost=$wid 149 | else 150 | # Get the whole window tree 151 | treeout=$(xwininfo -root -tree) 152 | 153 | if test -z "$treeout"; then 154 | echo 'Failed to get root window tree.' 155 | exit 1 156 | fi 157 | 158 | # Find the line number of the target window in the window tree 159 | lineno=$(echo -n "$treeout" | grep -nw "$wid" | head -n1 | cut -d ':' -f 1) 160 | 161 | if test -z "$lineno"; then 162 | echo 'Failed to find window in window tree.' 163 | exit 1 164 | fi 165 | 166 | # Find the highest ancestor of the target window below 167 | topmost=$(echo -n "$treeout" \ 168 | | head -n $((lineno + 1)) \ 169 | | sed -n 's/^ \(0x[[:xdigit:]]*\).*/\1/p' \ 170 | | tail -n 1) 171 | fi 172 | 173 | if test -z "$topmost"; then 174 | echo 'Failed to find the highest parent window below root of the' \ 175 | 'selected window.' 176 | exit 1 177 | fi 178 | 179 | # Remove the opacity property. 180 | if test x"$action" = x'delete'; then 181 | xprop -id "$topmost" -remove _NET_WM_WINDOW_OPACITY 182 | exit 0 183 | fi 184 | 185 | # Get current opacity. 186 | cur=$(xprop -id "$topmost" -notype _NET_WM_WINDOW_OPACITY \ 187 | | sed 's/^.*\b\([0-9]\+\).*$\|^.*$/\1/') 188 | test -z "$cur" && cur=0xffffffff 189 | cur=$((cur * 100 / 0xffffffff)) 190 | 191 | # Output current opacity. 192 | if test x"$action" = x'get'; then 193 | echo "$cur" 194 | exit 0 195 | fi 196 | 197 | # Calculate the desired opacity 198 | if echo "$opacity" | grep -q '^[+-]'; then 199 | opacity=$((cur + opacity)) 200 | fi 201 | 202 | test $opacity -lt 0 && opacity=0 203 | test $opacity -gt 100 && opacity=100 204 | 205 | # Set opacity 206 | opacity=$((opacity * 0xffffffff / 100)) 207 | xprop -id "$topmost" -f _NET_WM_WINDOW_OPACITY 32c \ 208 | -set _NET_WM_WINDOW_OPACITY "$opacity" 209 | -------------------------------------------------------------------------------- /compton-default-fshader-win.glsl: -------------------------------------------------------------------------------- 1 | uniform float opacity; 2 | uniform bool invert_color; 3 | uniform sampler2D tex; 4 | 5 | void main() { 6 | vec4 c = texture2D(tex, gl_TexCoord[0]); 7 | if (invert_color) 8 | c = vec4(vec3(c.a, c.a, c.a) - vec3(c), c.a); 9 | c *= opacity; 10 | gl_FragColor = c; 11 | } 12 | -------------------------------------------------------------------------------- /compton-fake-transparency-fshader-win.glsl: -------------------------------------------------------------------------------- 1 | uniform float opacity; 2 | uniform bool invert_color; 3 | uniform sampler2D tex; 4 | 5 | void main() { 6 | vec4 c = texture2D(tex, gl_TexCoord[0]); 7 | { 8 | // Change vec4(1.0, 1.0, 1.0, 1.0) to your desired color 9 | vec4 vdiff = abs(vec4(1.0, 1.0, 1.0, 1.0) - c); 10 | float diff = max(max(max(vdiff.r, vdiff.g), vdiff.b), vdiff.a); 11 | // Change 0.8 to your desired opacity 12 | if (diff < 0.001) 13 | c *= 0.8; 14 | } 15 | if (invert_color) 16 | c = vec4(vec3(c.a, c.a, c.a) - vec3(c), c.a); 17 | c *= opacity; 18 | gl_FragColor = c; 19 | } 20 | -------------------------------------------------------------------------------- /compton.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Type=Application 4 | Name=compton 5 | GenericName=X compositor 6 | Comment=A X compositor 7 | Categories=Utility; 8 | TryExec=compton 9 | Exec=compton 10 | Icon=compton 11 | # Thanks to quequotion for providing this file! 12 | -------------------------------------------------------------------------------- /compton.sample.conf: -------------------------------------------------------------------------------- 1 | # Shadow 2 | shadow = true; 3 | no-dnd-shadow = true; 4 | no-dock-shadow = true; 5 | clear-shadow = true; 6 | shadow-radius = 7; 7 | shadow-offset-x = -7; 8 | shadow-offset-y = -7; 9 | # shadow-opacity = 0.7; 10 | # shadow-red = 0.0; 11 | # shadow-green = 0.0; 12 | # shadow-blue = 0.0; 13 | shadow-exclude = [ 14 | "name = 'Notification'", 15 | "class_g = 'Conky'", 16 | "class_g ?= 'Notify-osd'", 17 | "class_g = 'Cairo-clock'", 18 | "_GTK_FRAME_EXTENTS@:c" 19 | ]; 20 | # shadow-exclude = "n:e:Notification"; 21 | # shadow-exclude-reg = "x10+0+0"; 22 | # xinerama-shadow-crop = true; 23 | 24 | # Opacity 25 | menu-opacity = 0.8; 26 | inactive-opacity = 0.8; 27 | # active-opacity = 0.8; 28 | frame-opacity = 0.7; 29 | inactive-opacity-override = false; 30 | alpha-step = 0.06; 31 | # inactive-dim = 0.2; 32 | # inactive-dim-fixed = true; 33 | # blur-background = true; 34 | # blur-background-frame = true; 35 | blur-kern = "3x3box"; 36 | # blur-kern = "5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; 37 | # blur-background-fixed = true; 38 | blur-background-exclude = [ 39 | "window_type = 'dock'", 40 | "window_type = 'desktop'", 41 | "_GTK_FRAME_EXTENTS@:c" 42 | ]; 43 | # opacity-rule = [ "80:class_g = 'URxvt'" ]; 44 | 45 | # Fading 46 | fading = true; 47 | # fade-delta = 30; 48 | fade-in-step = 0.03; 49 | fade-out-step = 0.03; 50 | # no-fading-openclose = true; 51 | # no-fading-destroyed-argb = true; 52 | fade-exclude = [ ]; 53 | 54 | # Other 55 | backend = "xrender"; 56 | mark-wmwin-focused = true; 57 | mark-ovredir-focused = true; 58 | # use-ewmh-active-win = true; 59 | detect-rounded-corners = true; 60 | detect-client-opacity = true; 61 | refresh-rate = 0; 62 | vsync = "none"; 63 | dbe = false; 64 | paint-on-overlay = true; 65 | # sw-opti = true; 66 | # unredir-if-possible = true; 67 | # unredir-if-possible-delay = 5000; 68 | # unredir-if-possible-exclude = [ ]; 69 | focus-exclude = [ "class_g = 'Cairo-clock'" ]; 70 | detect-transient = true; 71 | detect-client-leader = true; 72 | invert-color-include = [ ]; 73 | # resize-damage = 1; 74 | 75 | # GLX backend 76 | # glx-no-stencil = true; 77 | glx-copy-from-front = false; 78 | # glx-use-copysubbuffermesa = true; 79 | # glx-no-rebind-pixmap = true; 80 | glx-swap-method = "undefined"; 81 | # glx-use-gpushader4 = true; 82 | # xrender-sync = true; 83 | # xrender-sync-fence = true; 84 | 85 | # Window type settings 86 | wintypes: 87 | { 88 | tooltip = { fade = true; shadow = true; opacity = 0.75; focus = true; }; 89 | }; 90 | -------------------------------------------------------------------------------- /dbus-examples/cdbus-driver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -z "$SED" ]; then 4 | SED="sed" 5 | command -v gsed > /dev/null && SED="gsed" 6 | fi 7 | 8 | # === Get connection parameters === 9 | 10 | dpy=$(echo -n "$DISPLAY" | tr -c '[:alnum:]' _) 11 | 12 | if [ -z "$dpy" ]; then 13 | echo "Cannot find display." 14 | exit 1 15 | fi 16 | 17 | service="com.github.chjj.compton.${dpy}" 18 | interface='com.github.chjj.compton' 19 | object='/com/github/chjj/compton' 20 | type_win='uint32' 21 | type_enum='uint16' 22 | 23 | # === DBus methods === 24 | 25 | # List all window ID compton manages (except destroyed ones) 26 | dbus-send --print-reply --dest="$service" "$object" "${interface}.list_win" 27 | 28 | # Ensure we are tracking focus 29 | dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_set" string:track_focus boolean:true 30 | 31 | # Get window ID of currently focused window 32 | focused=$(dbus-send --print-reply --dest="$service" "$object" "${interface}.find_win" string:focused | $SED -n 's/^[[:space:]]*'${type_win}'[[:space:]]*\([[:digit:]]*\).*/\1/p') 33 | 34 | if [ -n "$focused" ]; then 35 | # Get invert_color_force property of the window 36 | dbus-send --print-reply --dest="$service" "$object" "${interface}.win_get" "${type_win}:${focused}" string:invert_color_force 37 | 38 | # Set the window to have inverted color 39 | dbus-send --print-reply --dest="$service" "$object" "${interface}.win_set" "${type_win}:${focused}" string:invert_color_force "${type_enum}:1" 40 | else 41 | echo "Cannot find focused window." 42 | fi 43 | 44 | # Set the clear_shadow setting to true 45 | dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_set" string:clear_shadow boolean:true 46 | 47 | # Get the clear_shadow setting 48 | dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_get" string:clear_shadow 49 | 50 | # Reset compton 51 | sleep 3 52 | dbus-send --print-reply --dest="$service" "$object" "${interface}.reset" 53 | 54 | # Undirect window 55 | sleep 3 56 | dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_set" string:redirected_force uint16:0 57 | 58 | # Revert back to auto 59 | sleep 3 60 | dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_set" string:redirected_force uint16:2 61 | 62 | # Force repaint 63 | dbus-send --print-reply --dest="$service" "$object" "${interface}.repaint" 64 | -------------------------------------------------------------------------------- /dbus-examples/inverter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # === Verify `compton --dbus` status === 4 | 5 | if [ -z "`dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep compton`" ]; then 6 | echo "compton DBus interface unavailable" 7 | if [ -n "`pgrep compton`" ]; then 8 | echo "compton running without dbus interface" 9 | #killall compton & # Causes all windows to flicker away and come back ugly. 10 | #compton --dbus & # Causes all windows to flicker away and come back beautiful 11 | else 12 | echo "compton not running" 13 | fi 14 | exit 1; 15 | fi 16 | 17 | # === Setup sed === 18 | 19 | if [ -z "$SED" ]; then 20 | SED="sed" 21 | command -v gsed > /dev/null && SED="gsed" 22 | fi 23 | 24 | # === Get connection parameters === 25 | 26 | dpy=$(echo -n "$DISPLAY" | tr -c '[:alnum:]' _) 27 | 28 | if [ -z "$dpy" ]; then 29 | echo "Cannot find display." 30 | exit 1; 31 | fi 32 | 33 | service="com.github.chjj.compton.${dpy}" 34 | interface="com.github.chjj.compton" 35 | compton_dbus="dbus-send --print-reply --dest="${service}" / "${interface}"." 36 | type_win='uint32' 37 | type_enum='uint16' 38 | 39 | # === Color Inversion === 40 | 41 | # Get window ID of window to invert 42 | if [ -z "$1" -o "$1" = "selected" ]; then 43 | window=$(xwininfo -frame | sed -n 's/^xwininfo: Window id: \(0x[[:xdigit:]][[:xdigit:]]*\).*/\1/p') # Select window by mouse 44 | elif [ "$1" = "focused" ]; then 45 | # Ensure we are tracking focus 46 | ${compton_dbus}opts_set string:track_focus boolean:true & 47 | window=$(${compton_dbus}find_win string:focused | $SED -n 's/^[[:space:]]*'${type_win}'[[:space:]]*\([[:digit:]]*\).*/\1/p') # Query compton for the active window 48 | elif echo "$1" | grep -Eiq '^([[:digit:]][[:digit:]]*|0x[[:xdigit:]][[:xdigit:]]*)$'; then 49 | window="$1" # Accept user-specified window-id if the format is correct 50 | else 51 | echo "$0" "[ selected | focused | window-id ]" 52 | fi 53 | 54 | # Color invert the selected or focused window 55 | if [ -n "$window" ]; then 56 | invert_status="$(${compton_dbus}win_get "${type_win}:${window}" string:invert_color | $SED -n 's/^[[:space:]]*boolean[[:space:]]*\([[:alpha:]]*\).*/\1/p')" 57 | if [ "$invert_status" = true ]; then 58 | invert=0 # Set the window to have normal color 59 | else 60 | invert=1 # Set the window to have inverted color 61 | fi 62 | ${compton_dbus}win_set "${type_win}:${window}" string:invert_color_force "${type_enum}:${invert}" & 63 | else 64 | echo "Cannot find $1 window." 65 | exit 1; 66 | fi 67 | exit 0; 68 | -------------------------------------------------------------------------------- /desc.txt: -------------------------------------------------------------------------------- 1 | Compton is a X compositing window manager, fork of xcompmgr-dana. 2 | -------------------------------------------------------------------------------- /functions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Shared functions for various supporting scripts of compton 4 | # Mostly copied from Gentoo gentoo-functions 5 | 6 | GOOD=$'\e[32;01m' 7 | WARN=$'\e[33;01m' 8 | BAD=$'\e[31;01m' 9 | HILITE=$'\e[36;01m' 10 | BRACKET=$'\e[34;01m' 11 | NORMAL=$'\e[0m' 12 | 13 | # @FUNCTION: eerror 14 | # @USAGE: [message] 15 | # @DESCRIPTION: 16 | # Show an error message. 17 | eerror() { 18 | echo -e "$@" | while read -r ; do 19 | echo " $BAD*$NORMAL $REPLY" >&2 20 | done 21 | return 0 22 | } 23 | 24 | # @FUNCTION: einfo 25 | # @USAGE: [message] 26 | # @DESCRIPTION: 27 | # Show a message. 28 | einfo() { 29 | echo -e "$@" | while read -r ; do 30 | echo " $GOOD*$NORMAL $REPLY" 31 | done 32 | return 0 33 | } 34 | 35 | # @FUNCTION: die 36 | # @USAGE: 37 | # @DESCRIPTION: 38 | # Print the call stack and the working directory, then quit the shell. 39 | die() { 40 | eerror "Call stack:" 41 | (( n = ${#FUNCNAME[@]} - 1 )) 42 | while (( n > 0 )); do 43 | funcname=${FUNCNAME[$((n - 1))]} 44 | sourcefile=$(basename ${BASH_SOURCE[${n}]}) 45 | lineno=${BASH_LINENO[$((n - 1))]} 46 | eerror " ${sourcefile}:${lineno} - Called ${funcname}()" 47 | (( n-- )) 48 | done 49 | eerror "Working directory: '$(pwd)'" 50 | exit 1 51 | } 52 | -------------------------------------------------------------------------------- /make-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Make a release source tarball containing pre-built documentation 4 | 5 | BASE_DIR=$(dirname "$0") 6 | . "${BASE_DIR}/functions.sh" 7 | 8 | main() { 9 | TMP=/tmp 10 | mkdir -p "${TMP}" 11 | 12 | VER="$(make version)" 13 | P="compton-${VER}" 14 | git archive --format=tar -o "${TMP}/${P}.tar" --prefix="${P}/" HEAD || die 15 | cd "${TMP}" || die 16 | tar xf "${TMP}/${P}.tar" || die 17 | sed -i "s/\(COMPTON_VERSION ?=\).*/\1 ${VER}/" "${P}/Makefile" || die 18 | cd "${P}" || die 19 | make docs || die 20 | cd .. || die 21 | tar cJf "${P}.tar.xz" "${P}" || die 22 | rm -r "${P}" "${P}.tar" || die 23 | einfo Archive is now on $(realpath ${P}.tar.xz) 24 | } 25 | 26 | main 27 | -------------------------------------------------------------------------------- /man/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | option(NEW_DOC "Build new man pages and HTML documentation" ON) 2 | 3 | # == Build documentation == 4 | # Stolen from https://issues.apache.org/jira/secure/attachment/12455612/AVRO-470.patch 5 | if (NEW_DOC) 6 | set (MAN_SRC 7 | compton.1.asciidoc 8 | compton-trans.1.asciidoc 9 | ) 10 | 11 | find_program(ASCIIDOC_EXEC asciidoc) 12 | find_program(ASCIIDOC_A2X_EXEC a2x) 13 | if (ASCIIDOC_EXEC AND ASCIIDOC_A2X_EXEC) 14 | foreach(_file ${MAN_SRC}) 15 | # get_filename_component() does not handle ".1.asciidoc" 16 | # correctly 17 | string(REPLACE ".asciidoc" "" _file_we "${_file}") 18 | set(_file_path "${CMAKE_CURRENT_SOURCE_DIR}/${_file}") 19 | set(_html_out "${_file_we}.html") 20 | set(_man_out "${_file_we}") 21 | add_custom_target(compton_man_${_file_we} ALL 22 | COMMAND ${ASCIIDOC_A2X_EXEC} --format manpage 23 | "${_file_path}" 24 | DEPENDS "${_file_path}" 25 | ) 26 | add_custom_command( 27 | OUTPUT "${_html_out}" 28 | COMMAND ${ASCIIDOC_EXEC} -o "${_html_out}" "${_file_path}" 29 | DEPENDS "${_file_path}" 30 | ) 31 | add_custom_target(compton_html_${_file_we} ALL 32 | DEPENDS "${_html_out}" 33 | ) 34 | endforeach(_file) 35 | else(ASCIIDOC_EXEC AND ASCIIDOC_A2X_EXEC) 36 | message(WARNING "asciidoc/a2x not found. New man pages and HTML documentation will not be built.") 37 | endif(ASCIIDOC_EXEC AND ASCIIDOC_A2X_EXEC) 38 | endif(NEW_DOC) 39 | 40 | # == Install == 41 | include(GNUInstallDirs) 42 | 43 | install(FILES 44 | "compton.1" 45 | "compton-trans.1" 46 | DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" COMPONENT doc) 47 | -------------------------------------------------------------------------------- /man/compton-trans.1.asciidoc: -------------------------------------------------------------------------------- 1 | compton-trans(1) 2 | ================ 3 | :doctype: manpage 4 | :man source: compton 5 | :man version: nightly-20121114 6 | :man manual: LOCAL USER COMMANDS 7 | 8 | NAME 9 | ---- 10 | compton-trans - an opacity setter tool 11 | 12 | SYNOPSIS 13 | -------- 14 | 15 | *compton-trans* [-w 'WINDOW_ID'] [-n 'WINDOW_NAME'] [-c] [-s] 'OPACITY' 16 | 17 | DESCRIPTION 18 | ----------- 19 | 20 | *compton-trans* is a bash script that sets '_NET_WM_WINDOW_OPACITY' attribute of a window using standard X11 command-line utilities, including *xprop*(1) and *xwininfo*(1). It is similar to *transset*(1) or *transset-df*(1). 21 | 22 | OPTIONS 23 | ------- 24 | *-w* 'WINDOW_ID':: 25 | Specify the window id of the target window. 26 | 27 | *-n* 'WINDOW_NAME':: 28 | Specify and try to match a window name. 29 | 30 | *-c*:: 31 | Specify the currently active window as target. Only works if EWMH '_NET_ACTIVE_WINDOW' property exists on root window. 32 | 33 | *-s*:: 34 | Select target window with mouse cursor. This is the default if no window has been specified. 35 | 36 | *-o* 'OPACITY':: 37 | Specify the new opacity value for the window. This value can be anywhere from 1-100. If it is prefixed with a plus or minus (+/-), this will increment or decrement from the target window's current opacity instead. 38 | 39 | EXAMPLES 40 | -------- 41 | 42 | * Set the opacity of the window with specific window ID to 75%: 43 | + 44 | ------------ 45 | compton-trans -w "$WINDOWID" 75 46 | ------------ 47 | 48 | * Set the opacity of the window with the name "urxvt" to 75%: 49 | + 50 | ------------ 51 | compton-trans -n "urxvt" 75 52 | ------------ 53 | 54 | * Set current window to opacity of 75%: 55 | + 56 | ------------ 57 | compton-trans -c 75 58 | ------------ 59 | 60 | * Select target window and set opacity to 75%: 61 | + 62 | ------------ 63 | compton-trans -s 75 64 | ------------ 65 | 66 | * Increment opacity of current active window by 5%: 67 | + 68 | ------------ 69 | compton-trans -c +5 70 | ------------ 71 | 72 | * Decrement opacity of current active window by 5%: 73 | + 74 | ------------ 75 | compton-trans -c -- -5 76 | ------------ 77 | 78 | BUGS 79 | ---- 80 | Please report any bugs you find to . 81 | 82 | AUTHORS 83 | ------- 84 | Christopher Jeffrey (). 85 | 86 | SEE ALSO 87 | -------- 88 | link:compton.1.html[*compton*(1)], *xprop*(1), *xwininfo*(1) 89 | -------------------------------------------------------------------------------- /man/compton.1.asciidoc: -------------------------------------------------------------------------------- 1 | compton(1) 2 | ========== 3 | :doctype: manpage 4 | :man source: compton 5 | :man version: nightly-20141124 6 | :man manual: LOCAL USER COMMANDS 7 | 8 | NAME 9 | ---- 10 | compton - a compositor for X11 11 | 12 | SYNOPSIS 13 | -------- 14 | *compton* ['OPTIONS'] 15 | 16 | WARNING 17 | ------- 18 | This man page may be less up-to-date than the usage text in compton (`compton -h`). 19 | 20 | DESCRIPTION 21 | ----------- 22 | compton is a compositor based on Dana Jansens' version of xcompmgr (which itself was written by Keith Packard). It includes some improvements over the original xcompmgr, like window frame opacity and inactive window transparency. 23 | 24 | OPTIONS 25 | ------- 26 | *-h*, *--help*:: 27 | Get the usage text embedded in program code, which may be more up-to-date than this man page. 28 | 29 | *-d* 'DISPLAY':: 30 | Display to be managed. 31 | 32 | *-r*, *--shadow-radius*='RADIUS':: 33 | The blur radius for shadows, in pixels. (defaults to 12) 34 | 35 | *-o*, *--shadow-opacity*='OPACITY':: 36 | The opacity of shadows. (0.0 - 1.0, defaults to 0.75) 37 | 38 | *-l*, *--shadow-offset-x*='OFFSET':: 39 | The left offset for shadows, in pixels. (defaults to -15) 40 | 41 | *-t*, *--shadow-offset-y*='OFFSET':: 42 | The top offset for shadows, in pixels. (defaults to -15) 43 | 44 | *-I*, *--fade-in-step*='OPACITY_STEP':: 45 | Opacity change between steps while fading in. (0.01 - 1.0, defaults to 0.028) 46 | 47 | *-O*, *--fade-out-step*='OPACITY_STEP':: 48 | Opacity change between steps while fading out. (0.01 - 1.0, defaults to 0.03) 49 | 50 | *-D*, *--fade-delta*='MILLISECONDS':: 51 | The time between steps in fade step, in milliseconds. (> 0, defaults to 10) 52 | 53 | *-m*, *--menu-opacity*='OPACITY':: 54 | Default opacity for dropdown menus and popup menus. (0.0 - 1.0, defaults to 1.0) 55 | 56 | *-c*, *--shadow*:: 57 | Enabled client-side shadows on windows. Note desktop windows (windows with '_NET_WM_WINDOW_TYPE_DESKTOP') never get shadow. 58 | 59 | *-C*, *--no-dock-shadow*:: 60 | Avoid drawing shadows on dock/panel windows. 61 | 62 | *-z*, *--clear-shadow*:: 63 | Zero the part of the shadow's mask behind the window. Note this may not work properly on ARGB windows with fully transparent areas. 64 | 65 | *-f*, *--fading*:: 66 | Fade windows in/out when opening/closing and when opacity changes, unless *--no-fading-openclose* is used. 67 | 68 | *-F*:: 69 | Equals to *-f*. Deprecated. 70 | 71 | *-i*, *--inactive-opacity*='OPACITY':: 72 | Opacity of inactive windows. (0.1 - 1.0, disabled by default) 73 | 74 | *-e*, *--frame-opacity*='OPACITY':: 75 | Opacity of window titlebars and borders. (0.1 - 1.0, disabled by default) 76 | 77 | *-G*, *--no-dnd-shadow*:: 78 | Don't draw shadows on drag-and-drop windows. 79 | 80 | *-b*, *--daemon*:: 81 | Daemonize process. Fork to background after initialization. Causes issues with certain (badly-written) drivers. 82 | 83 | *-S*:: 84 | Enable synchronous X operation (for debugging). 85 | 86 | *--show-all-xerrors*:: 87 | Show all X errors (for debugging). 88 | 89 | *--config* 'PATH':: 90 | Look for configuration file at the path. See *CONFIGURATION FILES* section below for where compton looks for a configuration file by default. Use `/dev/null` to avoid loading configuration file. 91 | 92 | *--write-pid-path* 'PATH':: 93 | Write process ID to a file. 94 | 95 | *--shadow-red* 'VALUE':: 96 | Red color value of shadow (0.0 - 1.0, defaults to 0). 97 | 98 | *--shadow-green* 'VALUE':: 99 | Green color value of shadow (0.0 - 1.0, defaults to 0). 100 | 101 | *--shadow-blue* 'VALUE':: 102 | Blue color value of shadow (0.0 - 1.0, defaults to 0). 103 | 104 | *--inactive-opacity-override*:: 105 | Let inactive opacity set by *-i* overrides the windows' '_NET_WM_OPACITY' values. 106 | 107 | *--active-opacity* 'OPACITY':: 108 | Default opacity for active windows. (0.0 - 1.0) 109 | 110 | *--inactive-dim* 'VALUE':: 111 | Dim inactive windows. (0.0 - 1.0, defaults to 0.0) 112 | 113 | *--mark-wmwin-focused*:: 114 | Try to detect WM windows (a non-override-redirect window with no child that has `WM_STATE`) and mark them as active. 115 | 116 | *--mark-ovredir-focused*:: 117 | Mark override-redirect windows that doesn't have a child window with `WM_STATE` focused. 118 | 119 | *--no-fading-openclose*:: 120 | Do not fade on window open/close. 121 | 122 | *--no-fading-destroyed-argb*:: 123 | Do not fade destroyed ARGB windows with WM frame. Workaround of bugs in Openbox, Fluxbox, etc. 124 | 125 | *--shadow-ignore-shaped*:: 126 | Do not paint shadows on shaped windows. Note shaped windows here means windows setting its shape through X Shape extension. Those using ARGB background is beyond our control. Deprecated, use `--shadow-exclude 'bounding_shaped'` or `--shadow-exclude 'bounding_shaped && !rounded_corners'` instead. 127 | 128 | *--detect-rounded-corners*:: 129 | Try to detect windows with rounded corners and don't consider them shaped windows. The accuracy is not very high, unfortunately. 130 | 131 | *--detect-client-opacity*:: 132 | Detect '_NET_WM_OPACITY' on client windows, useful for window managers not passing '_NET_WM_OPACITY' of client windows to frame windows. 133 | 134 | *--refresh-rate* 'REFRESH_RATE':: 135 | Specify refresh rate of the screen. If not specified or 0, compton will try detecting this with X RandR extension. 136 | 137 | *--vsync* 'VSYNC_METHOD':: 138 | Set VSync method. VSync methods currently available: 139 | + 140 | -- 141 | * 'none': No VSync 142 | * 'drm': VSync with 'DRM_IOCTL_WAIT_VBLANK'. May only work on some (DRI-based) drivers. 143 | * 'opengl': Try to VSync with 'SGI_video_sync' OpenGL extension. Only work on some drivers. 144 | * 'opengl-oml': Try to VSync with 'OML_sync_control' OpenGL extension. Only work on some drivers. 145 | * 'opengl-swc': Try to VSync with 'SGI_swap_control' OpenGL extension. Only work on some drivers. Works only with GLX backend. Known to be most effective on many drivers. Does not guarantee to control paint timing. 146 | * 'opengl-mswc': Try to VSync with 'MESA_swap_control' OpenGL extension. Basically the same as 'opengl-swc' above, except the extension we use. 147 | 148 | (Note some VSync methods may not be enabled at compile time.) 149 | -- 150 | 151 | *--vsync-aggressive*:: 152 | Attempt to send painting request before VBlank and do XFlush() during VBlank. Reported to work pretty terribly. This switch may be lifted out at any moment. 153 | 154 | *--alpha-step* 'VALUE':: 155 | X Render backend: Step for pregenerating alpha pictures. (0.01 - 1.0, defaults to 0.03) 156 | 157 | *--dbe*:: 158 | Enable DBE painting mode, intended to use with VSync to (hopefully) eliminate tearing. Reported to have no effect, though. 159 | 160 | *--paint-on-overlay*:: 161 | Painting on X Composite overlay window instead of on root window. 162 | 163 | *--sw-opti*:: 164 | Limit compton to repaint at most once every 1 / 'refresh_rate' second to boost performance. This should not be used with *--vsync* drm/opengl/opengl-oml as they essentially does *--sw-opti*'s job already, unless you wish to specify a lower refresh rate than the actual value. 165 | 166 | *--use-ewmh-active-win*:: 167 | Use EWMH '_NET_ACTIVE_WINDOW' to determine currently focused window, rather than listening to 'FocusIn'/'FocusOut' event. Might have more accuracy, provided that the WM supports it. 168 | 169 | *--respect-prop-shadow*:: 170 | Respect '_COMPTON_SHADOW'. This a prototype-level feature, which you must not rely on. 171 | 172 | *--unredir-if-possible*:: 173 | Unredirect all windows if a full-screen opaque window is detected, to maximize performance for full-screen windows. Known to cause flickering when redirecting/unredirecting windows. *--paint-on-overlay* may make the flickering less obvious. 174 | 175 | *--unredir-if-possible-delay* 'MILLISECONDS':: 176 | Delay before unredirecting the window, in milliseconds. Defaults to 0. 177 | 178 | *--unredir-if-possible-exclude* 'CONDITION':: 179 | Conditions of windows that shouldn't be considered full-screen for unredirecting screen. 180 | 181 | *--shadow-exclude* 'CONDITION':: 182 | Specify a list of conditions of windows that should have no shadow. 183 | 184 | *--fade-exclude* 'CONDITION':: 185 | Specify a list of conditions of windows that should not be faded. 186 | 187 | *--focus-exclude* 'CONDITION':: 188 | Specify a list of conditions of windows that should always be considered focused. 189 | 190 | *--inactive-dim-fixed*:: 191 | Use fixed inactive dim value, instead of adjusting according to window opacity. 192 | 193 | *--detect-transient*:: 194 | Use 'WM_TRANSIENT_FOR' to group windows, and consider windows in the same group focused at the same time. 195 | 196 | *--detect-client-leader*:: 197 | Use 'WM_CLIENT_LEADER' to group windows, and consider windows in the same group focused at the same time. 'WM_TRANSIENT_FOR' has higher priority if *--detect-transient* is enabled, too. 198 | 199 | *--blur-background*:: 200 | Blur background of semi-transparent / ARGB windows. Bad in performance, with driver-dependent behavior. The name of the switch may change without prior notifications. 201 | 202 | *--blur-background-frame*:: 203 | Blur background of windows when the window frame is not opaque. Implies *--blur-background*. Bad in performance, with driver-dependent behavior. The name may change. 204 | 205 | *--blur-background-fixed*:: 206 | Use fixed blur strength rather than adjusting according to window opacity. 207 | 208 | *--blur-kern* 'MATRIX':: 209 | Specify the blur convolution kernel, with the following format: 210 | + 211 | ---- 212 | WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5... 213 | ---- 214 | + 215 | The element in the center must not be included, it will be forever 1.0 or changing based on opacity, depending on whether you have `--blur-background-fixed`. Yet the automatic adjustment of blur factor may not work well with a custom blur kernel. 216 | + 217 | A 7x7 Gaussian blur kernel (sigma = 0.84089642) looks like: 218 | + 219 | ---- 220 | --blur-kern '7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003' 221 | ---- 222 | + 223 | May also be one of the predefined kernels: `3x3box` (default), `5x5box`, `7x7box`, `3x3gaussian`, `5x5gaussian`, `7x7gaussian`, `9x9gaussian`, `11x11gaussian`. All Gaussian kernels are generated with sigma = 0.84089642 . You may use the accompanied `compton-convgen.py` to generate blur kernels. 224 | 225 | *--blur-background-exclude* 'CONDITION':: 226 | Exclude conditions for background blur. 227 | 228 | *--resize-damage* 'INTEGER':: 229 | Resize damaged region by a specific number of pixels. A positive value enlarges it while a negative one shrinks it. If the value is positive, those additional pixels will not be actually painted to screen, only used in blur calculation, and such. (Due to technical limitations, with *--dbe* or *--glx-swap-method*, those pixels will still be incorrectly painted to screen.) Primarily used to fix the line corruption issues of blur, in which case you should use the blur radius value here (e.g. with a 3x3 kernel, you should use *--resize-damage* 1, with a 5x5 one you use *--resize-damage* 2, and so on). May or may not work with `--glx-no-stencil`. Shrinking doesn't function correctly. 230 | 231 | *--invert-color-include* 'CONDITION':: 232 | Specify a list of conditions of windows that should be painted with inverted color. Resource-hogging, and is not well tested. 233 | 234 | *--opacity-rule* 'OPACITY':'CONDITION':: 235 | Specify a list of opacity rules, in the format `PERCENT:PATTERN`, like `50:name *= "Firefox"`. compton-trans is recommended over this. Note we do not distinguish 100% and unset, and we don't make any guarantee about possible conflicts with other programs that set '_NET_WM_WINDOW_OPACITY' on frame or client windows. 236 | 237 | *--shadow-exclude-reg* 'GEOMETRY':: 238 | Specify a X geometry that describes the region in which shadow should not be painted in, such as a dock window region. Use `--shadow-exclude-reg x10+0-0`, for example, if the 10 pixels on the bottom of the screen should not have shadows painted on. 239 | 240 | *--xinerama-shadow-crop*:: 241 | Crop shadow of a window fully on a particular Xinerama screen to the screen. 242 | 243 | *--backend* 'BACKEND':: 244 | Specify the backend to use: `xrender`, `glx`, or `xr_glx_hybrid`. `xrender` is the default one. 245 | + 246 | -- 247 | * `xrender` backend performs all rendering operations with X Render extension. It is what `xcompmgr` uses, and is generally a safe fallback when you encounter rendering artifacts or instability. 248 | * `glx` (OpenGL) backend performs all rendering operations with OpenGL. It is more friendly to some VSync methods, and has significantly superior performance on color inversion (`--invert-color-include`) or blur (`--blur-background`). It requires proper OpenGL 2.0 support from your driver and hardware. You may wish to look at the GLX performance optimization options below. `--xrender-sync` and `--xrender-sync-fence` might be needed on some systems to avoid delay in changes of screen contents. 249 | * `xr_glx_hybrid` backend renders the updated screen contents with X Render and presents it on the screen with GLX. It attempts to address the rendering issues some users encountered with GLX backend and enables the better VSync of GLX backends. `--vsync-use-glfinish` might fix some rendering issues with this backend. 250 | -- 251 | 252 | *--glx-no-stencil*:: 253 | GLX backend: Avoid using stencil buffer, useful if you don't have a stencil buffer. Might cause incorrect opacity when rendering transparent content (but never practically happened) and may not work with *--blur-background*. My tests show a 15% performance boost. Recommended. 254 | 255 | *--glx-copy-from-front*:: 256 | GLX backend: Copy unmodified regions from front buffer instead of redrawing them all. My tests with nvidia-drivers show a 10% decrease in performance when the whole screen is modified, but a 20% increase when only 1/4 is. My tests on nouveau show terrible slowdown. Useful with `--glx-swap-method`, as well. 257 | 258 | *--glx-use-copysubbuffermesa*:: 259 | GLX backend: Use 'MESA_copy_sub_buffer' to do partial screen update. My tests on nouveau shows a 200% performance boost when only 1/4 of the screen is updated. May break VSync and is not available on some drivers. Overrides *--glx-copy-from-front*. 260 | 261 | *--glx-no-rebind-pixmap*:: 262 | GLX backend: Avoid rebinding pixmap on window damage. Probably could improve performance on rapid window content changes, but is known to break things on some drivers (LLVMpipe, xf86-video-intel, etc.). Recommended if it works. 263 | 264 | *--glx-swap-method* undefined/exchange/copy/3/4/5/6/buffer-age:: 265 | GLX backend: GLX buffer swap method we assume. Could be `undefined` (0), `copy` (1), `exchange` (2), 3-6, or `buffer-age` (-1). `undefined` is the slowest and the safest, and the default value. `copy` is fastest, but may fail on some drivers, 2-6 are gradually slower but safer (6 is still faster than 0). Usually, double buffer means 2, triple buffer means 3. `buffer-age` means auto-detect using 'GLX_EXT_buffer_age', supported by some drivers. Useless with *--glx-use-copysubbuffermesa*. Partially breaks `--resize-damage`. Defaults to `undefined`. 266 | 267 | *--glx-use-gpushader4*:: 268 | GLX backend: Use 'GL_EXT_gpu_shader4' for some optimization on blur GLSL code. My tests on GTX 670 show no noticeable effect. 269 | 270 | *--xrender-sync*:: 271 | Attempt to synchronize client applications' draw calls with `XSync()`, used on GLX backend to ensure up-to-date window content is painted. 272 | 273 | *--xrender-sync-fence*:: 274 | Additionally use X Sync fence to sync clients' draw calls. Needed on nvidia-drivers with GLX backend for some users. May be disabled at compile time with `NO_XSYNC=1`. 275 | 276 | *--glx-fshader-win* 'SHADER':: 277 | GLX backend: Use specified GLSL fragment shader for rendering window contents. See `compton-default-fshader-win.glsl` and `compton-fake-transparency-fshader-win.glsl` in the source tree for examples. 278 | 279 | *--force-win-blend*:: 280 | Force all windows to be painted with blending. Useful if you have a *--glx-fshader-win* that could turn opaque pixels transparent. 281 | 282 | *--dbus*:: 283 | Enable remote control via D-Bus. See the *D-BUS API* section below for more details. 284 | 285 | *--benchmark* 'CYCLES':: 286 | Benchmark mode. Repeatedly paint until reaching the specified cycles. 287 | 288 | *--benchmark-wid* 'WINDOW_ID':: 289 | Specify window ID to repaint in benchmark mode. If omitted or is 0, the whole screen is repainted. 290 | 291 | FORMAT OF CONDITIONS 292 | -------------------- 293 | Some options accept a condition string to match certain windows. A condition string is formed by one or more conditions, joined by logical operators. 294 | 295 | A condition with "exists" operator looks like this: 296 | 297 | [] : 298 | 299 | With equals operator it looks like: 300 | 301 | [] : = 302 | 303 | With greater-than/less-than operators it looks like: 304 | 305 | [] : 306 | 307 | 'NEGATION' (optional) is one or more exclamation marks; 308 | 309 | 'TARGET' is either a predefined target name, or the name of a window property to match. Supported predefined targets are `id`, `x`, `y`, `x2` (x + widthb), `y2`, `width`, `height`, `widthb` (width + 2 * `border_width`), `heightb`, `override_redirect`, `argb` (whether the window has an ARGB visual), `focused`, `wmwin` (whether the window looks like a WM window, i.e. has no child window with `WM_STATE` and is not override-redirected), `bounding_shaped`, `rounded_corners` (requires *--detect-rounded-corners*), `client` (ID of client window), `window_type` (window type in string), `leader` (ID of window leader), `name`, `class_g` (= `WM_CLASS[1]`), `class_i` (= `WM_CLASS[0]`), and `role`. 310 | 311 | 'CLIENT/FRAME' is a single `@` if the window attribute should be be looked up on client window, nothing if on frame window; 312 | 313 | 'INDEX' (optional) is the index number of the property to look up. For example, `[2]` means look at the third value in the property. Do not specify it for predefined targets. 314 | 315 | 'FORMAT' (optional) specifies the format of the property, 8, 16, or 32. On absence we use format X reports. Do not specify it for predefined or string targets. 316 | 317 | 'TYPE' is a single character representing the type of the property to match for: `c` for 'CARDINAL', `a` for 'ATOM', `w` for 'WINDOW', `d` for 'DRAWABLE', `s` for 'STRING' (and any other string types, such as 'UTF8_STRING'). Do not specify it for predefined targets. 318 | 319 | 'OP QUALIFIER' (optional), applicable only for equals operator, could be `?` (ignore-case). 320 | 321 | 'MATCH TYPE' (optional), applicable only for equals operator, could be nothing (exact match), `*` (match anywhere), `^` (match from start), `%` (wildcard), or `~` (PCRE regular expression). 322 | 323 | 'OPERATOR' is one of `=` (equals), `<`, `>`, `<=`, `=>`, or nothing (exists). Exists operator checks whether a property exists on a window (but for predefined targets, exists means != 0 then). 324 | 325 | 'PATTERN' is either an integer or a string enclosed by single or double quotes. Python-3-style escape sequences and raw string are supported in the string format. 326 | 327 | Supported logical operators are `&&` (and) and `||` (or). `&&` has higher precedence than `||`, left-to-right associativity. Use parentheses to change precedence. 328 | 329 | Examples: 330 | 331 | # If the window is focused 332 | focused 333 | focused = 1 334 | # If the window is not override-redirected 335 | !override_redirect 336 | override_redirect = false 337 | override_redirect != true 338 | override_redirect != 1 339 | # If the window is a menu 340 | window_type *= "menu" 341 | _NET_WM_WINDOW_TYPE@:a *= "MENU" 342 | # If the window name contains "Firefox", ignore case 343 | name *?= "Firefox" 344 | _NET_WM_NAME@:s *?= "Firefox" 345 | # If the window name ends with "Firefox" 346 | name %= "*Firefox" 347 | name ~= "Firefox$" 348 | # If the window has a property _COMPTON_SHADOW with value 0, type CARDINAL, 349 | # format 32, value 0, on its frame window 350 | _COMPTON_SHADOW:32c = 0 351 | # If the third value of _NET_FRAME_EXTENTS is less than 20, or there's no 352 | # _NET_FRAME_EXTENTS property on client window 353 | _NET_FRAME_EXTENTS@[2]:32c < 20 || !_NET_FRAME_EXTENTS@:32c 354 | # The pattern here will be parsed as "dd4" 355 | name = "\x64\x64\o64" 356 | # The pattern here will be parsed as "\x64\x64\x64" 357 | name = r"\x64\x64\o64" 358 | 359 | 360 | LEGACY FORMAT OF CONDITIONS 361 | --------------------------- 362 | 363 | This is the old condition format we once used. Support of this format might be removed in the future. 364 | 365 | condition = TARGET:TYPE[FLAGS]:PATTERN 366 | 367 | 'TARGET' is one of "n" (window name), "i" (window class instance), "g" (window general class), and "r" (window role). 368 | 369 | 'TYPE' is one of "e" (exact match), "a" (match anywhere), "s" (match from start), "w" (wildcard), and "p" (PCRE regular expressions, if compiled with the support). 370 | 371 | 'FLAGS' could be a series of flags. Currently the only defined flag is "i" (ignore case). 372 | 373 | 'PATTERN' is the actual pattern string. 374 | 375 | CONFIGURATION FILES 376 | ------------------- 377 | compton could read from a configuration file if libconfig support is compiled in. If *--config* is not used, compton will seek for a configuration file in `$XDG_CONFIG_HOME/compton.conf` (`~/.config/compton.conf`, usually), then `~/.compton.conf`, then `compton.conf` under `$XDG_CONFIG_DIRS` (often `/etc/xdg/compton.conf`). 378 | 379 | compton uses general libconfig configuration file format. A sample configuration file is available as `compton.sample.conf` in the source tree. Most commandline switches each could be replaced with an option in configuration file, thus documented above. Window-type-specific settings are exposed only in configuration file and has the following format: 380 | 381 | ------------ 382 | wintypes: 383 | { 384 | WINDOW_TYPE = { fade = BOOL; shadow = BOOL; opacity = FLOAT; focus = BOOL; }; 385 | }; 386 | ------------ 387 | 388 | 'WINDOW_TYPE' is one of the 15 window types defined in EWMH standard: "unknown", "desktop", "dock", "toolbar", "menu", "utility", "splash", "dialog", "normal", "dropdown_menu", "popup_menu", "tooltip", "notify", "combo", and "dnd". "fade" and "shadow" controls window-type-specific shadow and fade settings. "opacity" controls default opacity of the window type. "focus" controls whether the window of this type is to be always considered focused. (By default, all window types except "normal" and "dialog" has this on.) 389 | 390 | SIGNALS 391 | ------- 392 | 393 | * compton reinitializes itself upon receiving `SIGUSR1`. 394 | 395 | D-BUS API 396 | --------- 397 | 398 | It's possible to control compton via D-Bus messages, by running compton with *--dbus* and send messages to `com.github.chjj.compton.`. `` is the display used by compton, with all non-alphanumeric characters transformed to underscores. For `DISPLAY=:0.0` you should use `com.github.chjj.compton._0_0`, for example. 399 | 400 | The D-Bus methods and signals are not yet stable, thus undocumented right now. 401 | 402 | EXAMPLES 403 | -------- 404 | 405 | * Disable configuration file parsing: 406 | + 407 | ------------ 408 | $ compton --config /dev/null 409 | ------------ 410 | 411 | * Run compton with client-side shadow and fading, disable shadow on dock windows and drag-and-drop windows: 412 | + 413 | ------------ 414 | $ compton -cCGf 415 | ------------ 416 | 417 | * Same thing as above, plus making inactive windows 80% transparent, making frame 80% transparent, don't fade on window open/close, enable software optimization, and fork to background: 418 | + 419 | ------------ 420 | $ compton -bcCGf -i 0.8 -e 0.8 --no-fading-openclose --sw-opti 421 | ------------ 422 | 423 | * Draw white shadows: 424 | + 425 | ------------ 426 | $ compton -c --shadow-red 1 --shadow-green 1 --shadow-blue 1 427 | ------------ 428 | 429 | * Avoid drawing shadows on wbar window: 430 | + 431 | ------------ 432 | $ compton -c --shadow-exclude 'class_g = "wbar"' 433 | ------------ 434 | 435 | * Enable OpenGL SGI_swap_control VSync with GLX backend: 436 | + 437 | ------------ 438 | $ compton --backend glx --vsync opengl-swc 439 | ------------ 440 | 441 | BUGS 442 | ---- 443 | Please report any you find to . 444 | 445 | AUTHORS 446 | ------- 447 | xcompmgr, originally written by Keith Packard, with contributions from Matthew Allum, Eric Anholt, Dan Doel, Thomas Luebking, Matthew Hawn, Ely Levy, Phil Blundell, and Carl Worth. Compton by Christopher Jeffrey, based on Dana Jansens' original work, with contributions from Richard Grenville. 448 | 449 | RESOURCES 450 | --------- 451 | Homepage: 452 | 453 | SEE ALSO 454 | -------- 455 | *xcompmgr*(1), link:compton-trans.html[*compton-trans*(1)] 456 | -------------------------------------------------------------------------------- /media/compton.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 57 | 58 | 59 | 60 | 61 | 65 | 66 | 67 | 68 | 69 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /media/icons/48x48/compton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlackCapCoder/compton/dd5837b5e96eae82567592919d107522c7a53a92/media/icons/48x48/compton.png -------------------------------------------------------------------------------- /src/c2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Compton - a compositor for X11 3 | * 4 | * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard 5 | * 6 | * Copyright (c) 2011-2013, Christopher Jeffrey 7 | * See LICENSE for more information. 8 | * 9 | */ 10 | 11 | #include "c2.h" 12 | 13 | /** 14 | * Parse a condition string. 15 | */ 16 | c2_lptr_t * 17 | c2_parsed(session_t *ps, c2_lptr_t **pcondlst, const char *pattern, 18 | void *data) { 19 | if (!pattern) 20 | return NULL; 21 | 22 | // Parse the pattern 23 | c2_ptr_t result = C2_PTR_INIT; 24 | int offset = -1; 25 | 26 | if (strlen(pattern) >= 2 && ':' == pattern[1]) 27 | offset = c2_parse_legacy(ps, pattern, 0, &result); 28 | else 29 | offset = c2_parse_grp(ps, pattern, 0, &result, 0); 30 | 31 | if (offset < 0) { 32 | c2_freep(&result); 33 | return NULL; 34 | } 35 | 36 | // Insert to pcondlst 37 | { 38 | const static c2_lptr_t lptr_def = C2_LPTR_INIT; 39 | c2_lptr_t *plptr = malloc(sizeof(c2_lptr_t)); 40 | if (!plptr) 41 | printf_errfq(1, "(): Failed to allocate memory for new condition linked" 42 | " list element."); 43 | memcpy(plptr, &lptr_def, sizeof(c2_lptr_t)); 44 | plptr->ptr = result; 45 | plptr->data = data; 46 | if (pcondlst) { 47 | plptr->next = *pcondlst; 48 | *pcondlst = plptr; 49 | } 50 | 51 | #ifdef DEBUG_C2 52 | printf_dbgf("(\"%s\"): ", pattern); 53 | c2_dump(plptr->ptr); 54 | #endif 55 | 56 | return plptr; 57 | } 58 | } 59 | 60 | #undef c2_error 61 | #define c2_error(format, ...) do { \ 62 | printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \ 63 | ## __VA_ARGS__); \ 64 | return -1; \ 65 | } while(0) 66 | 67 | #define C2H_SKIP_SPACES() { while (isspace(pattern[offset])) ++offset; } 68 | 69 | /** 70 | * Parse a group in condition string. 71 | * 72 | * @return offset of next character in string 73 | */ 74 | static int 75 | c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level) { 76 | // Check for recursion levels 77 | if (level > C2_MAX_LEVELS) 78 | c2_error("Exceeded maximum recursion levels."); 79 | 80 | if (!pattern) 81 | return -1; 82 | 83 | // Expected end character 84 | const char endchar = (offset ? ')': '\0'); 85 | 86 | #undef c2_error 87 | #define c2_error(format, ...) do { \ 88 | printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \ 89 | ## __VA_ARGS__); \ 90 | goto c2_parse_grp_fail; \ 91 | } while(0) 92 | 93 | // We use a system that a maximum of 2 elements are kept. When we find 94 | // the third element, we combine the elements according to operator 95 | // precedence. This design limits operators to have at most two-levels 96 | // of precedence and fixed left-to-right associativity. 97 | 98 | // For storing branch operators. ops[0] is actually unused 99 | c2_b_op_t ops[3] = { }; 100 | // For storing elements 101 | c2_ptr_t eles[2] = { C2_PTR_INIT, C2_PTR_INIT }; 102 | // Index of next free element slot in eles 103 | int elei = 0; 104 | // Pointer to the position of next element 105 | c2_ptr_t *pele = eles; 106 | // Negation flag of next operator 107 | bool neg = false; 108 | // Whether we are expecting an element immediately, is true at first, or 109 | // after encountering a logical operator 110 | bool next_expected = true; 111 | 112 | // Parse the pattern character-by-character 113 | for (; pattern[offset]; ++offset) { 114 | assert(elei <= 2); 115 | 116 | // Jump over spaces 117 | if (isspace(pattern[offset])) 118 | continue; 119 | 120 | // Handle end of group 121 | if (')' == pattern[offset]) 122 | break; 123 | 124 | // Handle "!" 125 | if ('!' == pattern[offset]) { 126 | if (!next_expected) 127 | c2_error("Unexpected \"!\"."); 128 | 129 | neg = !neg; 130 | continue; 131 | } 132 | 133 | // Handle AND and OR 134 | if ('&' == pattern[offset] || '|' == pattern[offset]) { 135 | if (next_expected) 136 | c2_error("Unexpected logical operator."); 137 | 138 | next_expected = true; 139 | if (!mstrncmp("&&", pattern + offset)) { 140 | ops[elei] = C2_B_OAND; 141 | ++offset; 142 | } 143 | else if (!mstrncmp("||", pattern + offset)) { 144 | ops[elei] = C2_B_OOR; 145 | ++offset; 146 | } 147 | else 148 | c2_error("Illegal logical operator."); 149 | 150 | continue; 151 | } 152 | 153 | // Parsing an element 154 | if (!next_expected) 155 | c2_error("Unexpected expression."); 156 | 157 | assert(!elei || ops[elei]); 158 | 159 | // If we are out of space 160 | if (2 == elei) { 161 | --elei; 162 | // If the first operator has higher or equal precedence, combine 163 | // the first two elements 164 | if (c2h_b_opcmp(ops[1], ops[2]) >= 0) { 165 | eles[0] = c2h_comb_tree(ops[1], eles[0], eles[1]); 166 | c2_ptr_reset(&eles[1]); 167 | pele = &eles[elei]; 168 | ops[1] = ops[2]; 169 | } 170 | // Otherwise, combine the second and the incoming one 171 | else { 172 | eles[1] = c2h_comb_tree(ops[2], eles[1], C2_PTR_NULL); 173 | assert(eles[1].isbranch); 174 | pele = &eles[1].b->opr2; 175 | } 176 | // The last operator always needs to be reset 177 | ops[2] = C2_B_OUNDEFINED; 178 | } 179 | 180 | // It's a subgroup if it starts with '(' 181 | if ('(' == pattern[offset]) { 182 | if ((offset = c2_parse_grp(ps, pattern, offset + 1, pele, level + 1)) < 0) 183 | goto c2_parse_grp_fail; 184 | } 185 | // Otherwise it's a leaf 186 | else { 187 | if ((offset = c2_parse_target(ps, pattern, offset, pele)) < 0) 188 | goto c2_parse_grp_fail; 189 | 190 | assert(!pele->isbranch && !c2_ptr_isempty(*pele)); 191 | 192 | if ((offset = c2_parse_op(pattern, offset, pele)) < 0) 193 | goto c2_parse_grp_fail; 194 | 195 | if ((offset = c2_parse_pattern(ps, pattern, offset, pele)) < 0) 196 | goto c2_parse_grp_fail; 197 | 198 | if (!c2_l_postprocess(ps, pele->l)) 199 | goto c2_parse_grp_fail; 200 | } 201 | // Decrement offset -- we will increment it in loop update 202 | --offset; 203 | 204 | // Apply negation 205 | if (neg) { 206 | neg = false; 207 | if (pele->isbranch) 208 | pele->b->neg = !pele->b->neg; 209 | else 210 | pele->l->neg = !pele->l->neg; 211 | } 212 | 213 | next_expected = false; 214 | ++elei; 215 | pele = &eles[elei]; 216 | } 217 | 218 | // Wrong end character? 219 | if (pattern[offset] && !endchar) 220 | c2_error("Expected end of string but found '%c'.", pattern[offset]); 221 | if (!pattern[offset] && endchar) 222 | c2_error("Expected '%c' but found end of string.", endchar); 223 | 224 | // Handle end of group 225 | if (!elei) { 226 | c2_error("Empty group."); 227 | } 228 | else if (next_expected) { 229 | c2_error("Missing rule before end of group."); 230 | } 231 | else if (elei > 1) { 232 | assert(2 == elei); 233 | assert(ops[1]); 234 | eles[0] = c2h_comb_tree(ops[1], eles[0], eles[1]); 235 | c2_ptr_reset(&eles[1]); 236 | } 237 | 238 | *presult = eles[0]; 239 | 240 | if (')' == pattern[offset]) 241 | ++offset; 242 | 243 | return offset; 244 | 245 | c2_parse_grp_fail: 246 | c2_freep(&eles[0]); 247 | c2_freep(&eles[1]); 248 | 249 | return -1; 250 | } 251 | 252 | #undef c2_error 253 | #define c2_error(format, ...) do { \ 254 | printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \ 255 | ## __VA_ARGS__); \ 256 | return -1; \ 257 | } while(0) 258 | 259 | /** 260 | * Parse the target part of a rule. 261 | */ 262 | static int 263 | c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) { 264 | // Initialize leaf 265 | presult->isbranch = false; 266 | presult->l = malloc(sizeof(c2_l_t)); 267 | if (!presult->l) 268 | c2_error("Failed to allocate memory for new leaf."); 269 | 270 | c2_l_t * const pleaf = presult->l; 271 | memcpy(pleaf, &leaf_def, sizeof(c2_l_t)); 272 | 273 | // Parse negation marks 274 | while ('!' == pattern[offset]) { 275 | pleaf->neg = !pleaf->neg; 276 | ++offset; 277 | C2H_SKIP_SPACES(); 278 | } 279 | 280 | // Copy target name out 281 | unsigned tgtlen = 0; 282 | for (; pattern[offset] 283 | && (isalnum(pattern[offset]) || '_' == pattern[offset]); ++offset) { 284 | ++tgtlen; 285 | } 286 | if (!tgtlen) 287 | c2_error("Empty target."); 288 | pleaf->tgt = mstrncpy(&pattern[offset - tgtlen], tgtlen); 289 | 290 | // Check for predefined targets 291 | for (unsigned i = 1; i < sizeof(C2_PREDEFS) / sizeof(C2_PREDEFS[0]); ++i) { 292 | if (!strcmp(C2_PREDEFS[i].name, pleaf->tgt)) { 293 | pleaf->predef = i; 294 | pleaf->type = C2_PREDEFS[i].type; 295 | pleaf->format = C2_PREDEFS[i].format; 296 | break; 297 | } 298 | } 299 | 300 | // Alias for predefined targets 301 | if (!pleaf->predef) { 302 | #define TGTFILL(pdefid) \ 303 | (pleaf->predef = pdefid, \ 304 | pleaf->type = C2_PREDEFS[pdefid].type, \ 305 | pleaf->format = C2_PREDEFS[pdefid].format) 306 | 307 | // if (!strcmp("WM_NAME", tgt) || !strcmp("_NET_WM_NAME", tgt)) 308 | // TGTFILL(C2_L_PNAME); 309 | #undef TGTFILL 310 | 311 | // Alias for custom properties 312 | #define TGTFILL(target, type, format) \ 313 | (pleaf->target = mstrcpy(target), \ 314 | pleaf->type = type, \ 315 | pleaf->format = format) 316 | 317 | // if (!strcmp("SOME_ALIAS")) 318 | // TGTFILL("ALIAS_TEXT", C2_L_TSTRING, 32); 319 | #undef TGTFILL 320 | } 321 | 322 | C2H_SKIP_SPACES(); 323 | 324 | // Parse target-on-frame flag 325 | if ('@' == pattern[offset]) { 326 | pleaf->tgt_onframe = true; 327 | ++offset; 328 | C2H_SKIP_SPACES(); 329 | } 330 | 331 | // Parse index 332 | if ('[' == pattern[offset]) { 333 | offset++; 334 | 335 | C2H_SKIP_SPACES(); 336 | 337 | int index = -1; 338 | char *endptr = NULL; 339 | 340 | index = strtol(pattern + offset, &endptr, 0); 341 | 342 | if (!endptr || pattern + offset == endptr) 343 | c2_error("No index number found after bracket."); 344 | 345 | if (index < 0) 346 | c2_error("Index number invalid."); 347 | 348 | if (pleaf->predef) 349 | c2_error("Predefined targets can't have index."); 350 | 351 | pleaf->index = index; 352 | offset = endptr - pattern; 353 | 354 | C2H_SKIP_SPACES(); 355 | 356 | if (']' != pattern[offset]) 357 | c2_error("Index end marker not found."); 358 | 359 | ++offset; 360 | 361 | C2H_SKIP_SPACES(); 362 | } 363 | 364 | // Parse target type and format 365 | if (':' == pattern[offset]) { 366 | ++offset; 367 | C2H_SKIP_SPACES(); 368 | 369 | // Look for format 370 | bool hasformat = false; 371 | int format = 0; 372 | { 373 | char *endptr = NULL; 374 | format = strtol(pattern + offset, &endptr, 0); 375 | assert(endptr); 376 | if ((hasformat = (endptr && endptr != pattern + offset))) 377 | offset = endptr - pattern; 378 | C2H_SKIP_SPACES(); 379 | } 380 | 381 | // Look for type 382 | enum c2_l_type type = C2_L_TUNDEFINED; 383 | { 384 | switch (pattern[offset]) { 385 | case 'w': type = C2_L_TWINDOW; break; 386 | case 'd': type = C2_L_TDRAWABLE; break; 387 | case 'c': type = C2_L_TCARDINAL; break; 388 | case 's': type = C2_L_TSTRING; break; 389 | case 'a': type = C2_L_TATOM; break; 390 | default: c2_error("Invalid type character."); 391 | } 392 | 393 | if (type) { 394 | if (pleaf->predef) { 395 | printf_errf("(): Warning: Type specified for a default target will be ignored."); 396 | } 397 | else { 398 | if (pleaf->type && type != pleaf->type) 399 | printf_errf("(): Warning: Default type overridden on target."); 400 | pleaf->type = type; 401 | } 402 | } 403 | 404 | offset++; 405 | C2H_SKIP_SPACES(); 406 | } 407 | 408 | // Default format 409 | if (!pleaf->format) { 410 | switch (pleaf->type) { 411 | case C2_L_TWINDOW: 412 | case C2_L_TDRAWABLE: 413 | case C2_L_TATOM: 414 | pleaf->format = 32; break; 415 | case C2_L_TSTRING: 416 | pleaf->format = 8; break; 417 | default: 418 | break; 419 | } 420 | } 421 | 422 | // Write format 423 | if (hasformat) { 424 | if (pleaf->predef) 425 | printf_errf("(): Warning: Format \"%d\" specified on a default target will be ignored.", format); 426 | else if (C2_L_TSTRING == pleaf->type) 427 | printf_errf("(): Warning: Format \"%d\" specified on a string target will be ignored.", format); 428 | else { 429 | if (pleaf->format && pleaf->format != format) 430 | printf_err("Warning: Default format %d overridden on target.", 431 | pleaf->format); 432 | pleaf->format = format; 433 | } 434 | } 435 | } 436 | 437 | if (!pleaf->type) 438 | c2_error("Target type cannot be determined."); 439 | 440 | // if (!pleaf->predef && !pleaf->format && C2_L_TSTRING != pleaf->type) 441 | // c2_error("Target format cannot be determined."); 442 | 443 | if (pleaf->format && 8 != pleaf->format 444 | && 16 != pleaf->format && 32 != pleaf->format) 445 | c2_error("Invalid format."); 446 | 447 | return offset; 448 | } 449 | 450 | /** 451 | * Parse the operator part of a leaf. 452 | */ 453 | static int 454 | c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult) { 455 | c2_l_t * const pleaf = presult->l; 456 | 457 | // Parse negation marks 458 | C2H_SKIP_SPACES(); 459 | while ('!' == pattern[offset]) { 460 | pleaf->neg = !pleaf->neg; 461 | ++offset; 462 | C2H_SKIP_SPACES(); 463 | } 464 | 465 | // Parse qualifiers 466 | if ('*' == pattern[offset] || '^' == pattern[offset] 467 | || '%' == pattern[offset] || '~' == pattern[offset]) { 468 | switch (pattern[offset]) { 469 | case '*': pleaf->match = C2_L_MCONTAINS; break; 470 | case '^': pleaf->match = C2_L_MSTART; break; 471 | case '%': pleaf->match = C2_L_MWILDCARD; break; 472 | case '~': pleaf->match = C2_L_MPCRE; break; 473 | default: assert(0); 474 | } 475 | ++offset; 476 | C2H_SKIP_SPACES(); 477 | } 478 | 479 | // Parse flags 480 | while ('?' == pattern[offset]) { 481 | pleaf->match_ignorecase = true; 482 | ++offset; 483 | C2H_SKIP_SPACES(); 484 | } 485 | 486 | // Parse operator 487 | while ('=' == pattern[offset] || '>' == pattern[offset] 488 | || '<' == pattern[offset]) { 489 | if ('=' == pattern[offset] && C2_L_OGT == pleaf->op) 490 | pleaf->op = C2_L_OGTEQ; 491 | else if ('=' == pattern[offset] && C2_L_OLT == pleaf->op) 492 | pleaf->op = C2_L_OLTEQ; 493 | else if (pleaf->op) { 494 | c2_error("Duplicate operator."); 495 | } 496 | else { 497 | switch (pattern[offset]) { 498 | case '=': pleaf->op = C2_L_OEQ; break; 499 | case '>': pleaf->op = C2_L_OGT; break; 500 | case '<': pleaf->op = C2_L_OLT; break; 501 | default: assert(0); 502 | } 503 | } 504 | ++offset; 505 | C2H_SKIP_SPACES(); 506 | } 507 | 508 | // Check for problems 509 | if (C2_L_OEQ != pleaf->op && (pleaf->match || pleaf->match_ignorecase)) 510 | c2_error("Exists/greater-than/less-than operators cannot have a qualifier."); 511 | 512 | return offset; 513 | } 514 | 515 | /** 516 | * Parse the pattern part of a leaf. 517 | */ 518 | static int 519 | c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) { 520 | c2_l_t * const pleaf = presult->l; 521 | 522 | // Exists operator cannot have pattern 523 | if (!pleaf->op) 524 | return offset; 525 | 526 | C2H_SKIP_SPACES(); 527 | 528 | char *endptr = NULL; 529 | // Check for boolean patterns 530 | if (!strcmp_wd("true", &pattern[offset])) { 531 | pleaf->ptntype = C2_L_PTINT; 532 | pleaf->ptnint = true; 533 | offset += strlen("true"); 534 | } 535 | else if (!strcmp_wd("false", &pattern[offset])) { 536 | pleaf->ptntype = C2_L_PTINT; 537 | pleaf->ptnint = false; 538 | offset += strlen("false"); 539 | } 540 | // Check for integer patterns 541 | else if (pleaf->ptnint = strtol(pattern + offset, &endptr, 0), 542 | pattern + offset != endptr) { 543 | pleaf->ptntype = C2_L_PTINT; 544 | offset = endptr - pattern; 545 | // Make sure we are stopping at the end of a word 546 | if (isalnum(pattern[offset])) 547 | c2_error("Trailing characters after a numeric pattern."); 548 | } 549 | // Check for string patterns 550 | else { 551 | bool raw = false; 552 | char delim = '\0'; 553 | 554 | // String flags 555 | if ('r' == tolower(pattern[offset])) { 556 | raw = true; 557 | ++offset; 558 | C2H_SKIP_SPACES(); 559 | } 560 | 561 | // Check for delimiters 562 | if ('\"' == pattern[offset] || '\'' == pattern[offset]) { 563 | pleaf->ptntype = C2_L_PTSTRING; 564 | delim = pattern[offset]; 565 | ++offset; 566 | } 567 | 568 | if (C2_L_PTSTRING != pleaf->ptntype) 569 | c2_error("Invalid pattern type."); 570 | 571 | // Parse the string now 572 | // We can't determine the length of the pattern, so we use the length 573 | // to the end of the pattern string -- currently escape sequences 574 | // cannot be converted to a string longer than itself. 575 | char *tptnstr = malloc((strlen(pattern + offset) + 1) * sizeof(char)); 576 | char *ptptnstr = tptnstr; 577 | pleaf->ptnstr = tptnstr; 578 | for (; pattern[offset] && delim != pattern[offset]; ++offset) { 579 | // Handle escape sequences if it's not a raw string 580 | if ('\\' == pattern[offset] && !raw) { 581 | switch(pattern[++offset]) { 582 | case '\\': *(ptptnstr++) = '\\'; break; 583 | case '\'': *(ptptnstr++) = '\''; break; 584 | case '\"': *(ptptnstr++) = '\"'; break; 585 | case 'a': *(ptptnstr++) = '\a'; break; 586 | case 'b': *(ptptnstr++) = '\b'; break; 587 | case 'f': *(ptptnstr++) = '\f'; break; 588 | case 'n': *(ptptnstr++) = '\n'; break; 589 | case 'r': *(ptptnstr++) = '\r'; break; 590 | case 't': *(ptptnstr++) = '\t'; break; 591 | case 'v': *(ptptnstr++) = '\v'; break; 592 | case 'o': 593 | case 'x': 594 | { 595 | char *tstr = mstrncpy(pattern + offset + 1, 2); 596 | char *pstr = NULL; 597 | long val = strtol(tstr, &pstr, 598 | ('o' == pattern[offset] ? 8: 16)); 599 | free(tstr); 600 | if (pstr != &tstr[2] || val <= 0) 601 | c2_error("Invalid octal/hex escape sequence."); 602 | assert(val < 256 && val >= 0); 603 | *(ptptnstr++) = val; 604 | offset += 2; 605 | break; 606 | } 607 | default: c2_error("Invalid escape sequence."); 608 | } 609 | } 610 | else { 611 | *(ptptnstr++) = pattern[offset]; 612 | } 613 | } 614 | if (!pattern[offset]) 615 | c2_error("Premature end of pattern string."); 616 | ++offset; 617 | *ptptnstr = '\0'; 618 | pleaf->ptnstr = mstrcpy(tptnstr); 619 | free(tptnstr); 620 | } 621 | 622 | C2H_SKIP_SPACES(); 623 | 624 | if (!pleaf->ptntype) 625 | c2_error("Invalid pattern type."); 626 | 627 | // Check if the type is correct 628 | if (!(((C2_L_TSTRING == pleaf->type 629 | || C2_L_TATOM == pleaf->type) 630 | && C2_L_PTSTRING == pleaf->ptntype) 631 | || ((C2_L_TCARDINAL == pleaf->type 632 | || C2_L_TWINDOW == pleaf->type 633 | || C2_L_TDRAWABLE == pleaf->type) 634 | && C2_L_PTINT == pleaf->ptntype))) 635 | c2_error("Pattern type incompatible with target type."); 636 | 637 | if (C2_L_PTINT == pleaf->ptntype && pleaf->match) 638 | c2_error("Integer/boolean pattern cannot have operator qualifiers."); 639 | 640 | if (C2_L_PTINT == pleaf->ptntype && pleaf->match_ignorecase) 641 | c2_error("Integer/boolean pattern cannot have flags."); 642 | 643 | if (C2_L_PTSTRING == pleaf->ptntype 644 | && (C2_L_OGT == pleaf->op || C2_L_OGTEQ == pleaf->op 645 | || C2_L_OLT == pleaf->op || C2_L_OLTEQ == pleaf->op)) 646 | c2_error("String pattern cannot have an arithmetic operator."); 647 | 648 | return offset; 649 | } 650 | 651 | /** 652 | * Parse a condition with legacy syntax. 653 | */ 654 | static int 655 | c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) { 656 | unsigned plen = strlen(pattern + offset); 657 | 658 | if (plen < 4 || ':' != pattern[offset + 1] 659 | || !strchr(pattern + offset + 2, ':')) 660 | c2_error("Legacy parser: Invalid format."); 661 | 662 | // Allocate memory for new leaf 663 | c2_l_t *pleaf = malloc(sizeof(c2_l_t)); 664 | if (!pleaf) 665 | printf_errfq(1, "(): Failed to allocate memory for new leaf."); 666 | presult->isbranch = false; 667 | presult->l = pleaf; 668 | memcpy(pleaf, &leaf_def, sizeof(c2_l_t)); 669 | pleaf->type = C2_L_TSTRING; 670 | pleaf->op = C2_L_OEQ; 671 | pleaf->ptntype = C2_L_PTSTRING; 672 | 673 | // Determine the pattern target 674 | #define TGTFILL(pdefid) \ 675 | (pleaf->predef = pdefid, \ 676 | pleaf->type = C2_PREDEFS[pdefid].type, \ 677 | pleaf->format = C2_PREDEFS[pdefid].format) 678 | switch (pattern[offset]) { 679 | case 'n': TGTFILL(C2_L_PNAME); break; 680 | case 'i': TGTFILL(C2_L_PCLASSI); break; 681 | case 'g': TGTFILL(C2_L_PCLASSG); break; 682 | case 'r': TGTFILL(C2_L_PROLE); break; 683 | default: c2_error("Target \"%c\" invalid.\n", pattern[offset]); 684 | } 685 | #undef TGTFILL 686 | 687 | offset += 2; 688 | 689 | // Determine the match type 690 | switch (pattern[offset]) { 691 | case 'e': pleaf->match = C2_L_MEXACT; break; 692 | case 'a': pleaf->match = C2_L_MCONTAINS; break; 693 | case 's': pleaf->match = C2_L_MSTART; break; 694 | case 'w': pleaf->match = C2_L_MWILDCARD; break; 695 | case 'p': pleaf->match = C2_L_MPCRE; break; 696 | default: c2_error("Type \"%c\" invalid.\n", pattern[offset]); 697 | } 698 | ++offset; 699 | 700 | // Determine the pattern flags 701 | while (':' != pattern[offset]) { 702 | switch (pattern[offset]) { 703 | case 'i': pleaf->match_ignorecase = true; break; 704 | default: c2_error("Flag \"%c\" invalid.", pattern[offset]); 705 | } 706 | ++offset; 707 | } 708 | ++offset; 709 | 710 | // Copy the pattern 711 | pleaf->ptnstr = mstrcpy(pattern + offset); 712 | 713 | if (!c2_l_postprocess(ps, pleaf)) 714 | return -1; 715 | 716 | return offset; 717 | } 718 | 719 | #undef c2_error 720 | #define c2_error(format, ...) { \ 721 | printf_err(format, ## __VA_ARGS__); \ 722 | return false; } 723 | 724 | /** 725 | * Do postprocessing on a condition leaf. 726 | */ 727 | static bool 728 | c2_l_postprocess(session_t *ps, c2_l_t *pleaf) { 729 | // Give a pattern type to a leaf with exists operator, if needed 730 | if (C2_L_OEXISTS == pleaf->op && !pleaf->ptntype) { 731 | pleaf->ptntype = 732 | (C2_L_TSTRING == pleaf->type ? C2_L_PTSTRING: C2_L_PTINT); 733 | } 734 | 735 | // Get target atom if it's not a predefined one 736 | if (!pleaf->predef) { 737 | pleaf->tgtatom = get_atom(ps, pleaf->tgt); 738 | if (!pleaf->tgtatom) 739 | c2_error("Failed to get atom for target \"%s\".", pleaf->tgt); 740 | } 741 | 742 | // Insert target Atom into atom track list 743 | if (pleaf->tgtatom) { 744 | bool found = false; 745 | for (latom_t *platom = ps->track_atom_lst; platom; 746 | platom = platom->next) { 747 | if (pleaf->tgtatom == platom->atom) { 748 | found = true; 749 | break; 750 | } 751 | } 752 | if (!found) { 753 | latom_t *pnew = malloc(sizeof(latom_t)); 754 | if (!pnew) 755 | printf_errfq(1, "(): Failed to allocate memory for new track atom."); 756 | pnew->next = ps->track_atom_lst; 757 | pnew->atom = pleaf->tgtatom; 758 | ps->track_atom_lst = pnew; 759 | } 760 | } 761 | 762 | // Enable specific tracking options in compton if needed by the condition 763 | // TODO: Add track_leader 764 | if (pleaf->predef) { 765 | switch (pleaf->predef) { 766 | case C2_L_PFOCUSED: ps->o.track_focus = true; break; 767 | // case C2_L_PROUNDED: ps->o.detect_rounded_corners = true; break; 768 | case C2_L_PNAME: 769 | case C2_L_PCLASSG: 770 | case C2_L_PCLASSI: 771 | case C2_L_PROLE: ps->o.track_wdata = true; break; 772 | default: break; 773 | } 774 | } 775 | 776 | // Warn about lower case characters in target name 777 | if (!pleaf->predef) { 778 | for (const char *pc = pleaf->tgt; *pc; ++pc) { 779 | if (islower(*pc)) { 780 | printf_errf("(): Warning: Lowercase character in target name \"%s\".", pleaf->tgt); 781 | break; 782 | } 783 | } 784 | } 785 | 786 | // PCRE patterns 787 | if (C2_L_PTSTRING == pleaf->ptntype && C2_L_MPCRE == pleaf->match) { 788 | #ifdef CONFIG_REGEX_PCRE 789 | const char *error = NULL; 790 | int erroffset = 0; 791 | int options = 0; 792 | 793 | // Ignore case flag 794 | if (pleaf->match_ignorecase) 795 | options |= PCRE_CASELESS; 796 | 797 | // Compile PCRE expression 798 | pleaf->regex_pcre = pcre_compile(pleaf->ptnstr, options, 799 | &error, &erroffset, NULL); 800 | if (!pleaf->regex_pcre) 801 | c2_error("Pattern \"%s\": PCRE regular expression parsing failed on " 802 | "offset %d: %s", pleaf->ptnstr, erroffset, error); 803 | #ifdef CONFIG_REGEX_PCRE_JIT 804 | pleaf->regex_pcre_extra = pcre_study(pleaf->regex_pcre, 805 | PCRE_STUDY_JIT_COMPILE, &error); 806 | if (!pleaf->regex_pcre_extra) { 807 | printf("Pattern \"%s\": PCRE regular expression study failed: %s", 808 | pleaf->ptnstr, error); 809 | } 810 | #endif 811 | 812 | // Free the target string 813 | // free(pleaf->tgt); 814 | // pleaf->tgt = NULL; 815 | #else 816 | c2_error("PCRE regular expression support not compiled in."); 817 | #endif 818 | } 819 | 820 | return true; 821 | } 822 | /** 823 | * Free a condition tree. 824 | */ 825 | static void 826 | c2_free(c2_ptr_t p) { 827 | // For a branch element 828 | if (p.isbranch) { 829 | c2_b_t * const pbranch = p.b; 830 | 831 | if (!pbranch) 832 | return; 833 | 834 | c2_free(pbranch->opr1); 835 | c2_free(pbranch->opr2); 836 | free(pbranch); 837 | } 838 | // For a leaf element 839 | else { 840 | c2_l_t * const pleaf = p.l; 841 | 842 | if (!pleaf) 843 | return; 844 | 845 | free(pleaf->tgt); 846 | free(pleaf->ptnstr); 847 | #ifdef CONFIG_REGEX_PCRE 848 | pcre_free(pleaf->regex_pcre); 849 | LPCRE_FREE_STUDY(pleaf->regex_pcre_extra); 850 | #endif 851 | free(pleaf); 852 | } 853 | } 854 | 855 | /** 856 | * Free a condition tree in c2_lptr_t. 857 | */ 858 | c2_lptr_t * 859 | c2_free_lptr(c2_lptr_t *lp) { 860 | if (!lp) 861 | return NULL; 862 | 863 | c2_lptr_t *pnext = lp->next; 864 | c2_free(lp->ptr); 865 | free(lp); 866 | 867 | return pnext; 868 | } 869 | 870 | /** 871 | * Get a string representation of a rule target. 872 | */ 873 | static const char * 874 | c2h_dump_str_tgt(const c2_l_t *pleaf) { 875 | if (pleaf->predef) 876 | return C2_PREDEFS[pleaf->predef].name; 877 | else 878 | return pleaf->tgt; 879 | } 880 | 881 | /** 882 | * Get a string representation of a target. 883 | */ 884 | static const char * 885 | c2h_dump_str_type(const c2_l_t *pleaf) { 886 | switch (pleaf->type) { 887 | case C2_L_TWINDOW: return "w"; 888 | case C2_L_TDRAWABLE: return "d"; 889 | case C2_L_TCARDINAL: return "c"; 890 | case C2_L_TSTRING: return "s"; 891 | case C2_L_TATOM: return "a"; 892 | case C2_L_TUNDEFINED: break; 893 | } 894 | 895 | return NULL; 896 | } 897 | 898 | /** 899 | * Dump a condition tree. 900 | */ 901 | static void 902 | c2_dump_raw(c2_ptr_t p) { 903 | // For a branch 904 | if (p.isbranch) { 905 | const c2_b_t * const pbranch = p.b; 906 | 907 | if (!pbranch) 908 | return; 909 | 910 | if (pbranch->neg) 911 | putchar('!'); 912 | 913 | printf("("); 914 | c2_dump_raw(pbranch->opr1); 915 | 916 | switch (pbranch->op) { 917 | case C2_B_OAND: printf(" && "); break; 918 | case C2_B_OOR: printf(" || "); break; 919 | case C2_B_OXOR: printf(" XOR "); break; 920 | default: assert(0); break; 921 | } 922 | 923 | c2_dump_raw(pbranch->opr2); 924 | printf(")"); 925 | } 926 | // For a leaf 927 | else { 928 | const c2_l_t * const pleaf = p.l; 929 | 930 | if (!pleaf) 931 | return; 932 | 933 | if (C2_L_OEXISTS == pleaf->op && pleaf->neg) 934 | putchar('!'); 935 | 936 | // Print target name, type, and format 937 | { 938 | printf("%s", c2h_dump_str_tgt(pleaf)); 939 | if (pleaf->tgt_onframe) 940 | putchar('@'); 941 | if (pleaf->index >= 0) 942 | printf("[%d]", pleaf->index); 943 | printf(":%d%s", pleaf->format, c2h_dump_str_type(pleaf)); 944 | } 945 | 946 | // Print operator 947 | putchar(' '); 948 | 949 | if (C2_L_OEXISTS != pleaf->op && pleaf->neg) 950 | putchar('!'); 951 | 952 | switch (pleaf->match) { 953 | case C2_L_MEXACT: break; 954 | case C2_L_MCONTAINS: putchar('*'); break; 955 | case C2_L_MSTART: putchar('^'); break; 956 | case C2_L_MPCRE: putchar('~'); break; 957 | case C2_L_MWILDCARD: putchar('%'); break; 958 | } 959 | 960 | if (pleaf->match_ignorecase) 961 | putchar('?'); 962 | 963 | switch (pleaf->op) { 964 | case C2_L_OEXISTS: break; 965 | case C2_L_OEQ: fputs("=", stdout); break; 966 | case C2_L_OGT: fputs(">", stdout); break; 967 | case C2_L_OGTEQ: fputs(">=", stdout); break; 968 | case C2_L_OLT: fputs("<", stdout); break; 969 | case C2_L_OLTEQ: fputs("<=", stdout); break; 970 | } 971 | 972 | if (C2_L_OEXISTS == pleaf->op) 973 | return; 974 | 975 | // Print pattern 976 | putchar(' '); 977 | switch (pleaf->ptntype) { 978 | case C2_L_PTINT: 979 | printf("%ld", pleaf->ptnint); 980 | break; 981 | case C2_L_PTSTRING: 982 | // TODO: Escape string before printing out? 983 | printf("\"%s\"", pleaf->ptnstr); 984 | break; 985 | default: 986 | assert(0); 987 | break; 988 | } 989 | } 990 | } 991 | 992 | /** 993 | * Get the type atom of a condition. 994 | */ 995 | static Atom 996 | c2_get_atom_type(const c2_l_t *pleaf) { 997 | switch (pleaf->type) { 998 | case C2_L_TCARDINAL: 999 | return XA_CARDINAL; 1000 | case C2_L_TWINDOW: 1001 | return XA_WINDOW; 1002 | case C2_L_TSTRING: 1003 | return XA_STRING; 1004 | case C2_L_TATOM: 1005 | return XA_ATOM; 1006 | case C2_L_TDRAWABLE: 1007 | return XA_DRAWABLE; 1008 | default: 1009 | assert(0); 1010 | break; 1011 | } 1012 | 1013 | assert(0); 1014 | return AnyPropertyType; 1015 | } 1016 | 1017 | /** 1018 | * Match a window against a single leaf window condition. 1019 | * 1020 | * For internal use. 1021 | */ 1022 | static inline void 1023 | c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf, 1024 | bool *pres, bool *perr) { 1025 | assert(pleaf); 1026 | 1027 | const Window wid = (pleaf->tgt_onframe ? w->client_win: w->id); 1028 | 1029 | // Return if wid is missing 1030 | if (!pleaf->predef && !wid) 1031 | return; 1032 | 1033 | const int idx = (pleaf->index < 0 ? 0: pleaf->index); 1034 | 1035 | switch (pleaf->ptntype) { 1036 | // Deal with integer patterns 1037 | case C2_L_PTINT: 1038 | { 1039 | long tgt = 0; 1040 | 1041 | // Get the value 1042 | // A predefined target 1043 | if (pleaf->predef) { 1044 | *perr = false; 1045 | switch (pleaf->predef) { 1046 | case C2_L_PID: tgt = wid; break; 1047 | case C2_L_PX: tgt = w->a.x; break; 1048 | case C2_L_PY: tgt = w->a.y; break; 1049 | case C2_L_PX2: tgt = w->a.x + w->widthb; break; 1050 | case C2_L_PY2: tgt = w->a.y + w->heightb; break; 1051 | case C2_L_PWIDTH: tgt = w->a.width; break; 1052 | case C2_L_PHEIGHT: tgt = w->a.height; break; 1053 | case C2_L_PWIDTHB: tgt = w->widthb; break; 1054 | case C2_L_PHEIGHTB: tgt = w->heightb; break; 1055 | case C2_L_PBDW: tgt = w->a.border_width; break; 1056 | case C2_L_PFULLSCREEN: tgt = win_is_fullscreen(ps, w); break; 1057 | case C2_L_POVREDIR: tgt = w->a.override_redirect; break; 1058 | case C2_L_PARGB: tgt = (WMODE_ARGB == w->mode); break; 1059 | case C2_L_PFOCUSED: tgt = win_is_focused_real(ps, w); break; 1060 | case C2_L_PWMWIN: tgt = w->wmwin; break; 1061 | case C2_L_PBSHAPED: tgt = w->bounding_shaped; break; 1062 | case C2_L_PROUNDED: tgt = w->rounded_corners; break; 1063 | case C2_L_PCLIENT: tgt = w->client_win; break; 1064 | case C2_L_PLEADER: tgt = w->leader; break; 1065 | default: *perr = true; assert(0); break; 1066 | } 1067 | } 1068 | // A raw window property 1069 | else { 1070 | winprop_t prop = wid_get_prop_adv(ps, wid, pleaf->tgtatom, 1071 | idx, 1L, c2_get_atom_type(pleaf), pleaf->format); 1072 | if (prop.nitems) { 1073 | *perr = false; 1074 | tgt = winprop_get_int(prop); 1075 | } 1076 | free_winprop(&prop); 1077 | } 1078 | 1079 | if (*perr) 1080 | return; 1081 | 1082 | // Do comparison 1083 | switch (pleaf->op) { 1084 | case C2_L_OEXISTS: 1085 | *pres = (pleaf->predef ? tgt: true); 1086 | break; 1087 | case C2_L_OEQ: *pres = (tgt == pleaf->ptnint); break; 1088 | case C2_L_OGT: *pres = (tgt > pleaf->ptnint); break; 1089 | case C2_L_OGTEQ: *pres = (tgt >= pleaf->ptnint); break; 1090 | case C2_L_OLT: *pres = (tgt < pleaf->ptnint); break; 1091 | case C2_L_OLTEQ: *pres = (tgt <= pleaf->ptnint); break; 1092 | default: *perr = true; assert(0); break; 1093 | } 1094 | } 1095 | break; 1096 | // String patterns 1097 | case C2_L_PTSTRING: 1098 | { 1099 | const char *tgt = NULL; 1100 | char *tgt_free = NULL; 1101 | 1102 | // A predefined target 1103 | if (pleaf->predef) { 1104 | switch (pleaf->predef) { 1105 | case C2_L_PWINDOWTYPE: tgt = WINTYPES[w->window_type]; 1106 | break; 1107 | case C2_L_PNAME: tgt = w->name; break; 1108 | case C2_L_PCLASSG: tgt = w->class_general; break; 1109 | case C2_L_PCLASSI: tgt = w->class_instance; break; 1110 | case C2_L_PROLE: tgt = w->role; break; 1111 | default: assert(0); break; 1112 | } 1113 | } 1114 | // If it's an atom type property, convert atom to string 1115 | else if (C2_L_TATOM == pleaf->type) { 1116 | winprop_t prop = wid_get_prop_adv(ps, wid, pleaf->tgtatom, 1117 | idx, 1L, c2_get_atom_type(pleaf), pleaf->format); 1118 | Atom atom = winprop_get_int(prop); 1119 | if (atom) { 1120 | tgt_free = XGetAtomName(ps->dpy, atom); 1121 | } 1122 | if (tgt_free) { 1123 | tgt = tgt_free; 1124 | } 1125 | free_winprop(&prop); 1126 | } 1127 | // Otherwise, just fetch the string list 1128 | else { 1129 | char **strlst = NULL; 1130 | int nstr; 1131 | if (wid_get_text_prop(ps, wid, pleaf->tgtatom, &strlst, 1132 | &nstr) && nstr > idx) { 1133 | tgt_free = mstrcpy(strlst[idx]); 1134 | tgt = tgt_free; 1135 | } 1136 | if (strlst) 1137 | XFreeStringList(strlst); 1138 | } 1139 | 1140 | if (tgt) { 1141 | *perr = false; 1142 | } 1143 | else { 1144 | return; 1145 | } 1146 | 1147 | // Actual matching 1148 | switch (pleaf->op) { 1149 | case C2_L_OEXISTS: 1150 | *pres = true; 1151 | break; 1152 | case C2_L_OEQ: 1153 | switch (pleaf->match) { 1154 | case C2_L_MEXACT: 1155 | if (pleaf->match_ignorecase) 1156 | *pres = !strcasecmp(tgt, pleaf->ptnstr); 1157 | else 1158 | *pres = !strcmp(tgt, pleaf->ptnstr); 1159 | break; 1160 | case C2_L_MCONTAINS: 1161 | if (pleaf->match_ignorecase) 1162 | *pres = strcasestr(tgt, pleaf->ptnstr); 1163 | else 1164 | *pres = strstr(tgt, pleaf->ptnstr); 1165 | break; 1166 | case C2_L_MSTART: 1167 | if (pleaf->match_ignorecase) 1168 | *pres = !strncasecmp(tgt, pleaf->ptnstr, 1169 | strlen(pleaf->ptnstr)); 1170 | else 1171 | *pres = !strncmp(tgt, pleaf->ptnstr, 1172 | strlen(pleaf->ptnstr)); 1173 | break; 1174 | case C2_L_MWILDCARD: 1175 | { 1176 | int flags = 0; 1177 | if (pleaf->match_ignorecase) 1178 | flags |= FNM_CASEFOLD; 1179 | *pres = !fnmatch(pleaf->ptnstr, tgt, flags); 1180 | } 1181 | break; 1182 | case C2_L_MPCRE: 1183 | #ifdef CONFIG_REGEX_PCRE 1184 | *pres = (pcre_exec(pleaf->regex_pcre, 1185 | pleaf->regex_pcre_extra, 1186 | tgt, strlen(tgt), 0, 0, NULL, 0) >= 0); 1187 | #else 1188 | assert(0); 1189 | #endif 1190 | break; 1191 | } 1192 | break; 1193 | default: 1194 | *perr = true; 1195 | assert(0); 1196 | } 1197 | 1198 | // Free the string after usage, if necessary 1199 | if (tgt_free) { 1200 | if (C2_L_TATOM == pleaf->type) 1201 | cxfree(tgt_free); 1202 | else 1203 | free(tgt_free); 1204 | } 1205 | } 1206 | break; 1207 | default: 1208 | assert(0); 1209 | break; 1210 | } 1211 | } 1212 | 1213 | /** 1214 | * Match a window against a single window condition. 1215 | * 1216 | * @return true if matched, false otherwise. 1217 | */ 1218 | static bool 1219 | c2_match_once(session_t *ps, win *w, const c2_ptr_t cond) { 1220 | bool result = false; 1221 | bool error = true; 1222 | 1223 | // Handle a branch 1224 | if (cond.isbranch) { 1225 | const c2_b_t *pb = cond.b; 1226 | 1227 | if (!pb) 1228 | return false; 1229 | 1230 | error = false; 1231 | 1232 | switch (pb->op) { 1233 | case C2_B_OAND: 1234 | result = (c2_match_once(ps, w, pb->opr1) 1235 | && c2_match_once(ps, w, pb->opr2)); 1236 | break; 1237 | case C2_B_OOR: 1238 | result = (c2_match_once(ps, w, pb->opr1) 1239 | || c2_match_once(ps, w, pb->opr2)); 1240 | break; 1241 | case C2_B_OXOR: 1242 | result = (c2_match_once(ps, w, pb->opr1) 1243 | != c2_match_once(ps, w, pb->opr2)); 1244 | break; 1245 | default: 1246 | error = true; 1247 | assert(0); 1248 | } 1249 | 1250 | #ifdef DEBUG_WINMATCH 1251 | printf_dbgf("(%#010lx): branch: result = %d, pattern = ", w->id, result); 1252 | c2_dump(cond); 1253 | #endif 1254 | } 1255 | // Handle a leaf 1256 | else { 1257 | const c2_l_t *pleaf = cond.l; 1258 | 1259 | if (!pleaf) 1260 | return false; 1261 | 1262 | c2_match_once_leaf(ps, w, pleaf, &result, &error); 1263 | 1264 | // For EXISTS operator, no errors are fatal 1265 | if (C2_L_OEXISTS == pleaf->op && error) { 1266 | result = false; 1267 | error = false; 1268 | } 1269 | 1270 | #ifdef DEBUG_WINMATCH 1271 | printf_dbgf("(%#010lx): leaf: result = %d, error = %d, " 1272 | "client = %#010lx, pattern = ", 1273 | w->id, result, error, w->client_win); 1274 | c2_dump(cond); 1275 | #endif 1276 | } 1277 | 1278 | // Postprocess the result 1279 | if (error) 1280 | result = false; 1281 | 1282 | if (cond.isbranch ? cond.b->neg: cond.l->neg) 1283 | result = !result; 1284 | 1285 | return result; 1286 | } 1287 | 1288 | /** 1289 | * Match a window against a condition linked list. 1290 | * 1291 | * @param cache a place to cache the last matched condition 1292 | * @param pdata a place to return the data 1293 | * @return true if matched, false otherwise. 1294 | */ 1295 | bool 1296 | c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst, 1297 | const c2_lptr_t **cache, void **pdata) { 1298 | assert(IsViewable == w->a.map_state); 1299 | 1300 | // Check if the cached entry matches firstly 1301 | if (cache && *cache && c2_match_once(ps, w, (*cache)->ptr)) { 1302 | if (pdata) 1303 | *pdata = (*cache)->data; 1304 | return true; 1305 | } 1306 | 1307 | // Then go through the whole linked list 1308 | for (; condlst; condlst = condlst->next) { 1309 | if (c2_match_once(ps, w, condlst->ptr)) { 1310 | if (cache) 1311 | *cache = condlst; 1312 | if (pdata) 1313 | *pdata = condlst->data; 1314 | return true; 1315 | } 1316 | } 1317 | 1318 | return false; 1319 | } 1320 | 1321 | -------------------------------------------------------------------------------- /src/c2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Compton - a compositor for X11 3 | * 4 | * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard 5 | * 6 | * Copyright (c) 2011-2013, Christopher Jeffrey 7 | * See LICENSE for more information. 8 | * 9 | */ 10 | 11 | #include "common.h" 12 | 13 | #include 14 | #include 15 | 16 | // libpcre 17 | #ifdef CONFIG_REGEX_PCRE 18 | #include 19 | 20 | // For compatiblity with opr1 = p1; 263 | p.b->opr2 = p2; 264 | p.b->op = op; 265 | 266 | return p; 267 | } 268 | 269 | /** 270 | * Get the precedence value of a condition branch operator. 271 | */ 272 | static inline int 273 | c2h_b_opp(c2_b_op_t op) { 274 | switch (op) { 275 | case C2_B_OAND: return 2; 276 | case C2_B_OOR: return 1; 277 | case C2_B_OXOR: return 1; 278 | default: break; 279 | } 280 | 281 | assert(0); 282 | return 0; 283 | } 284 | 285 | /** 286 | * Compare precedence of two condition branch operators. 287 | * 288 | * Associativity is left-to-right, forever. 289 | * 290 | * @return positive number if op1 > op2, 0 if op1 == op2 in precedence, 291 | * negative number otherwise 292 | */ 293 | static inline int 294 | c2h_b_opcmp(c2_b_op_t op1, c2_b_op_t op2) { 295 | return c2h_b_opp(op1) - c2h_b_opp(op2); 296 | } 297 | 298 | static int 299 | c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level); 300 | 301 | static int 302 | c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult); 303 | 304 | static int 305 | c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult); 306 | 307 | static int 308 | c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult); 309 | 310 | static int 311 | c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult); 312 | 313 | static bool 314 | c2_l_postprocess(session_t *ps, c2_l_t *pleaf); 315 | 316 | static void 317 | c2_free(c2_ptr_t p); 318 | 319 | /** 320 | * Wrapper of c2_free(). 321 | */ 322 | static inline void 323 | c2_freep(c2_ptr_t *pp) { 324 | if (pp) { 325 | c2_free(*pp); 326 | c2_ptr_reset(pp); 327 | } 328 | } 329 | 330 | static const char * 331 | c2h_dump_str_tgt(const c2_l_t *pleaf); 332 | 333 | static const char * 334 | c2h_dump_str_type(const c2_l_t *pleaf); 335 | 336 | static void 337 | c2_dump_raw(c2_ptr_t p); 338 | 339 | /** 340 | * Wrapper of c2_dump_raw(). 341 | */ 342 | static inline void 343 | c2_dump(c2_ptr_t p) { 344 | c2_dump_raw(p); 345 | printf("\n"); 346 | fflush(stdout); 347 | } 348 | 349 | static Atom 350 | c2_get_atom_type(const c2_l_t *pleaf); 351 | 352 | static bool 353 | c2_match_once(session_t *ps, win *w, const c2_ptr_t cond); 354 | 355 | -------------------------------------------------------------------------------- /src/compton.h: -------------------------------------------------------------------------------- 1 | /** 2 | * compton.h 3 | */ 4 | 5 | // Throw everything in here. 6 | 7 | 8 | // === Includes === 9 | 10 | #include "common.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #ifdef CONFIG_VSYNC_DRM 21 | #include 22 | // We references some definitions in drm.h, which could also be found in 23 | // /usr/src/linux/include/drm/drm.h, but that path is probably even less 24 | // reliable than libdrm 25 | #include 26 | #include 27 | #include 28 | #endif 29 | 30 | // == Functions == 31 | 32 | // inline functions must be made static to compile correctly under clang: 33 | // http://clang.llvm.org/compatibility.html#inline 34 | 35 | // Helper functions 36 | 37 | static void 38 | discard_ignore(session_t *ps, unsigned long sequence); 39 | 40 | static void 41 | set_ignore(session_t *ps, unsigned long sequence); 42 | 43 | /** 44 | * Ignore X errors caused by next X request. 45 | */ 46 | static inline void 47 | set_ignore_next(session_t *ps) { 48 | set_ignore(ps, NextRequest(ps->dpy)); 49 | } 50 | 51 | static int 52 | should_ignore(session_t *ps, unsigned long sequence); 53 | 54 | /** 55 | * Reset filter on a Picture. 56 | */ 57 | static inline void 58 | xrfilter_reset(session_t *ps, Picture p) { 59 | XRenderSetPictureFilter(ps->dpy, p, "Nearest", NULL, 0); 60 | } 61 | 62 | /** 63 | * Subtract two unsigned long values. 64 | * 65 | * Truncate to 0 if the result is negative. 66 | */ 67 | static inline unsigned long __attribute__((const)) 68 | sub_unslong(unsigned long a, unsigned long b) { 69 | return (a > b) ? a - b : 0; 70 | } 71 | 72 | /** 73 | * Set a bool array of all wintypes to true. 74 | */ 75 | static inline void 76 | wintype_arr_enable(bool arr[]) { 77 | wintype_t i; 78 | 79 | for (i = 0; i < NUM_WINTYPES; ++i) { 80 | arr[i] = true; 81 | } 82 | } 83 | 84 | /** 85 | * Set a switch_t array of all unset wintypes to true. 86 | */ 87 | static inline void 88 | wintype_arr_enable_unset(switch_t arr[]) { 89 | wintype_t i; 90 | 91 | for (i = 0; i < NUM_WINTYPES; ++i) 92 | if (UNSET == arr[i]) 93 | arr[i] = ON; 94 | } 95 | 96 | /** 97 | * Check if a window ID exists in an array of window IDs. 98 | * 99 | * @param arr the array of window IDs 100 | * @param count amount of elements in the array 101 | * @param wid window ID to search for 102 | */ 103 | static inline bool 104 | array_wid_exists(const Window *arr, int count, Window wid) { 105 | while (count--) { 106 | if (arr[count] == wid) { 107 | return true; 108 | } 109 | } 110 | 111 | return false; 112 | } 113 | 114 | /** 115 | * Convert a geometry_t value to XRectangle. 116 | */ 117 | static inline XRectangle 118 | geom_to_rect(session_t *ps, const geometry_t *src, const XRectangle *def) { 119 | XRectangle rect_def = { .x = 0, .y = 0, 120 | .width = ps->root_width, .height = ps->root_height }; 121 | if (!def) def = &rect_def; 122 | 123 | XRectangle rect = { .x = src->x, .y = src->y, 124 | .width = src->wid, .height = src->hei }; 125 | if (src->wid < 0) rect.width = def->width; 126 | if (src->hei < 0) rect.height = def->height; 127 | if (-1 == src->x) rect.x = def->x; 128 | else if (src->x < 0) rect.x = ps->root_width + rect.x + 2 - rect.width; 129 | if (-1 == src->y) rect.y = def->y; 130 | else if (src->y < 0) rect.y = ps->root_height + rect.y + 2 - rect.height; 131 | return rect; 132 | } 133 | 134 | /** 135 | * Convert a XRectangle to a XServerRegion. 136 | */ 137 | static inline XserverRegion 138 | rect_to_reg(session_t *ps, const XRectangle *src) { 139 | if (!src) return None; 140 | XRectangle bound = { .x = 0, .y = 0, 141 | .width = ps->root_width, .height = ps->root_height }; 142 | XRectangle res = { }; 143 | rect_crop(&res, src, &bound); 144 | if (res.width && res.height) 145 | return XFixesCreateRegion(ps->dpy, &res, 1); 146 | return None; 147 | } 148 | 149 | /** 150 | * Destroy a Picture. 151 | */ 152 | inline static void 153 | free_picture(session_t *ps, Picture *p) { 154 | if (*p) { 155 | XRenderFreePicture(ps->dpy, *p); 156 | *p = None; 157 | } 158 | } 159 | 160 | /** 161 | * Destroy a Pixmap. 162 | */ 163 | inline static void 164 | free_pixmap(session_t *ps, Pixmap *p) { 165 | if (*p) { 166 | XFreePixmap(ps->dpy, *p); 167 | *p = None; 168 | } 169 | } 170 | 171 | /** 172 | * Destroy a Damage. 173 | */ 174 | inline static void 175 | free_damage(session_t *ps, Damage *p) { 176 | if (*p) { 177 | // BadDamage will be thrown if the window is destroyed 178 | set_ignore_next(ps); 179 | XDamageDestroy(ps->dpy, *p); 180 | *p = None; 181 | } 182 | } 183 | 184 | /** 185 | * Destroy a condition list. 186 | */ 187 | static inline void 188 | free_wincondlst(c2_lptr_t **pcondlst) { 189 | #ifdef CONFIG_C2 190 | while ((*pcondlst = c2_free_lptr(*pcondlst))) 191 | continue; 192 | #endif 193 | } 194 | 195 | /** 196 | * Free Xinerama screen info. 197 | */ 198 | static inline void 199 | free_xinerama_info(session_t *ps) { 200 | #ifdef CONFIG_XINERAMA 201 | if (ps->xinerama_scr_regs) { 202 | for (int i = 0; i < ps->xinerama_nscrs; ++i) 203 | free_region(ps, &ps->xinerama_scr_regs[i]); 204 | free(ps->xinerama_scr_regs); 205 | } 206 | cxfree(ps->xinerama_scrs); 207 | ps->xinerama_scrs = NULL; 208 | ps->xinerama_nscrs = 0; 209 | #endif 210 | } 211 | 212 | /** 213 | * Check whether a paint_t contains enough data. 214 | */ 215 | static inline bool 216 | paint_isvalid(session_t *ps, const paint_t *ppaint) { 217 | // Don't check for presence of Pixmap here, because older X Composite doesn't 218 | // provide it 219 | if (!ppaint) 220 | return false; 221 | 222 | if (bkend_use_xrender(ps) && !ppaint->pict) 223 | return false; 224 | 225 | #ifdef CONFIG_VSYNC_OPENGL 226 | if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex, None)) 227 | return false; 228 | #endif 229 | 230 | return true; 231 | } 232 | 233 | /** 234 | * Bind texture in paint_t if we are using GLX backend. 235 | */ 236 | static inline bool 237 | paint_bind_tex_real(session_t *ps, paint_t *ppaint, 238 | unsigned wid, unsigned hei, unsigned depth, bool force) { 239 | #ifdef CONFIG_VSYNC_OPENGL 240 | if (!ppaint->pixmap) 241 | return false; 242 | 243 | if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap)) 244 | return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth); 245 | #endif 246 | 247 | return true; 248 | } 249 | 250 | static inline bool 251 | paint_bind_tex(session_t *ps, paint_t *ppaint, 252 | unsigned wid, unsigned hei, unsigned depth, bool force) { 253 | if (BKEND_GLX == ps->o.backend) 254 | return paint_bind_tex_real(ps, ppaint, wid, hei, depth, force); 255 | return true; 256 | } 257 | 258 | /** 259 | * Free data in a reg_data_t. 260 | */ 261 | static inline void 262 | free_reg_data(reg_data_t *pregd) { 263 | cxfree(pregd->rects); 264 | pregd->rects = NULL; 265 | pregd->nrects = 0; 266 | } 267 | 268 | /** 269 | * Free paint_t. 270 | */ 271 | static inline void 272 | free_paint(session_t *ps, paint_t *ppaint) { 273 | free_paint_glx(ps, ppaint); 274 | free_picture(ps, &ppaint->pict); 275 | free_pixmap(ps, &ppaint->pixmap); 276 | } 277 | 278 | /** 279 | * Free w->paint. 280 | */ 281 | static inline void 282 | free_wpaint(session_t *ps, win *w) { 283 | free_paint(ps, &w->paint); 284 | free_fence(ps, &w->fence); 285 | } 286 | 287 | /** 288 | * Destroy all resources in a struct _win. 289 | */ 290 | static inline void 291 | free_win_res(session_t *ps, win *w) { 292 | free_win_res_glx(ps, w); 293 | free_region(ps, &w->extents); 294 | free_paint(ps, &w->paint); 295 | free_region(ps, &w->border_size); 296 | free_paint(ps, &w->shadow_paint); 297 | free_damage(ps, &w->damage); 298 | free_region(ps, &w->reg_ignore); 299 | free(w->name); 300 | free(w->class_instance); 301 | free(w->class_general); 302 | free(w->role); 303 | } 304 | 305 | /** 306 | * Free root tile related things. 307 | */ 308 | static inline void 309 | free_root_tile(session_t *ps) { 310 | free_picture(ps, &ps->root_tile_paint.pict); 311 | free_texture(ps, &ps->root_tile_paint.ptex); 312 | if (ps->root_tile_fill) 313 | free_pixmap(ps, &ps->root_tile_paint.pixmap); 314 | ps->root_tile_paint.pixmap = None; 315 | ps->root_tile_fill = false; 316 | } 317 | 318 | /** 319 | * Get current system clock in milliseconds. 320 | */ 321 | static inline time_ms_t 322 | get_time_ms(void) { 323 | struct timeval tv; 324 | 325 | gettimeofday(&tv, NULL); 326 | 327 | return tv.tv_sec % SEC_WRAP * 1000 + tv.tv_usec / 1000; 328 | } 329 | 330 | /** 331 | * Convert time from milliseconds to struct timeval. 332 | */ 333 | static inline struct timeval 334 | ms_to_tv(int timeout) { 335 | return (struct timeval) { 336 | .tv_sec = timeout / MS_PER_SEC, 337 | .tv_usec = timeout % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC) 338 | }; 339 | } 340 | 341 | /** 342 | * Whether an event is DamageNotify. 343 | */ 344 | static inline bool 345 | isdamagenotify(session_t *ps, const XEvent *ev) { 346 | return ps->damage_event + XDamageNotify == ev->type; 347 | } 348 | 349 | /** 350 | * Create a XTextProperty of a single string. 351 | */ 352 | static inline XTextProperty * 353 | make_text_prop(session_t *ps, char *str) { 354 | XTextProperty *pprop = ccalloc(1, XTextProperty); 355 | 356 | if (XmbTextListToTextProperty(ps->dpy, &str, 1, XStringStyle, pprop)) { 357 | cxfree(pprop->value); 358 | free(pprop); 359 | pprop = NULL; 360 | } 361 | 362 | return pprop; 363 | } 364 | 365 | 366 | /** 367 | * Set a single-string text property on a window. 368 | */ 369 | static inline bool 370 | wid_set_text_prop(session_t *ps, Window wid, Atom prop_atom, char *str) { 371 | XTextProperty *pprop = make_text_prop(ps, str); 372 | if (!pprop) { 373 | printf_errf("(\"%s\"): Failed to make text property.", str); 374 | return false; 375 | } 376 | 377 | XSetTextProperty(ps->dpy, wid, pprop, prop_atom); 378 | cxfree(pprop->value); 379 | cxfree(pprop); 380 | 381 | return true; 382 | } 383 | 384 | static void 385 | run_fade(session_t *ps, win *w, unsigned steps); 386 | 387 | static void 388 | set_fade_callback(session_t *ps, win *w, 389 | void (*callback) (session_t *ps, win *w), bool exec_callback); 390 | 391 | /** 392 | * Execute fade callback of a window if fading finished. 393 | */ 394 | static inline void 395 | check_fade_fin(session_t *ps, win *w) { 396 | if (w->fade_callback && w->opacity == w->opacity_tgt) { 397 | // Must be the last line as the callback could destroy w! 398 | set_fade_callback(ps, w, NULL, true); 399 | } 400 | } 401 | 402 | static void 403 | set_fade_callback(session_t *ps, win *w, 404 | void (*callback) (session_t *ps, win *w), bool exec_callback); 405 | 406 | static double 407 | gaussian(double r, double x, double y); 408 | 409 | static conv * 410 | make_gaussian_map(double r); 411 | 412 | static unsigned char 413 | sum_gaussian(conv *map, double opacity, 414 | int x, int y, int width, int height); 415 | 416 | static void 417 | presum_gaussian(session_t *ps, conv *map); 418 | 419 | static XImage * 420 | make_shadow(session_t *ps, double opacity, int width, int height); 421 | 422 | static bool 423 | win_build_shadow(session_t *ps, win *w, double opacity); 424 | 425 | static Picture 426 | solid_picture(session_t *ps, bool argb, double a, 427 | double r, double g, double b); 428 | 429 | /** 430 | * Stop listening for events on a particular window. 431 | */ 432 | static inline void 433 | win_ev_stop(session_t *ps, win *w) { 434 | // Will get BadWindow if the window is destroyed 435 | set_ignore_next(ps); 436 | XSelectInput(ps->dpy, w->id, 0); 437 | 438 | if (w->client_win) { 439 | set_ignore_next(ps); 440 | XSelectInput(ps->dpy, w->client_win, 0); 441 | } 442 | 443 | if (ps->shape_exists) { 444 | set_ignore_next(ps); 445 | XShapeSelectInput(ps->dpy, w->id, 0); 446 | } 447 | } 448 | 449 | /** 450 | * Get the children of a window. 451 | * 452 | * @param ps current session 453 | * @param w window to check 454 | * @param children [out] an array of child window IDs 455 | * @param nchildren [out] number of children 456 | * @return 1 if successful, 0 otherwise 457 | */ 458 | static inline bool 459 | wid_get_children(session_t *ps, Window w, 460 | Window **children, unsigned *nchildren) { 461 | Window troot, tparent; 462 | 463 | if (!XQueryTree(ps->dpy, w, &troot, &tparent, children, nchildren)) { 464 | *nchildren = 0; 465 | return false; 466 | } 467 | 468 | return true; 469 | } 470 | 471 | /** 472 | * Check if a window is bounding-shaped. 473 | */ 474 | static inline bool 475 | wid_bounding_shaped(const session_t *ps, Window wid) { 476 | if (ps->shape_exists) { 477 | Bool bounding_shaped = False, clip_shaped = False; 478 | int x_bounding, y_bounding, x_clip, y_clip; 479 | unsigned int w_bounding, h_bounding, w_clip, h_clip; 480 | 481 | XShapeQueryExtents(ps->dpy, wid, &bounding_shaped, 482 | &x_bounding, &y_bounding, &w_bounding, &h_bounding, 483 | &clip_shaped, &x_clip, &y_clip, &w_clip, &h_clip); 484 | return bounding_shaped; 485 | } 486 | 487 | return false; 488 | } 489 | 490 | /** 491 | * Determine if a window change affects reg_ignore and set 492 | * reg_ignore_expire accordingly. 493 | */ 494 | static inline void 495 | update_reg_ignore_expire(session_t *ps, const win *w) { 496 | if (w->to_paint && WMODE_SOLID == w->mode) 497 | ps->reg_ignore_expire = true; 498 | } 499 | 500 | /** 501 | * Check whether a window has WM frames. 502 | */ 503 | static inline bool __attribute__((pure)) 504 | win_has_frame(const win *w) { 505 | return w->a.border_width 506 | || w->frame_extents.top || w->frame_extents.left 507 | || w->frame_extents.right || w->frame_extents.bottom; 508 | } 509 | 510 | /** 511 | * Calculate the extents of the frame of the given window based on EWMH 512 | * _NET_FRAME_EXTENTS and the X window border width. 513 | */ 514 | static inline margin_t __attribute__((pure)) 515 | win_calc_frame_extents(session_t *ps, const win *w) { 516 | margin_t result = w->frame_extents; 517 | result.top = max_i(result.top, w->a.border_width); 518 | result.left = max_i(result.left, w->a.border_width); 519 | result.bottom = max_i(result.bottom, w->a.border_width); 520 | result.right = max_i(result.right, w->a.border_width); 521 | return result; 522 | } 523 | 524 | static inline void 525 | wid_set_opacity_prop(session_t *ps, Window wid, opacity_t val) { 526 | const unsigned long v = val; 527 | XChangeProperty(ps->dpy, wid, ps->atom_opacity, XA_CARDINAL, 32, 528 | PropModeReplace, (unsigned char *) &v, 1); 529 | } 530 | 531 | static inline void 532 | wid_rm_opacity_prop(session_t *ps, Window wid) { 533 | XDeleteProperty(ps->dpy, wid, ps->atom_opacity); 534 | } 535 | 536 | /** 537 | * Dump an drawable's info. 538 | */ 539 | static inline void 540 | dump_drawable(session_t *ps, Drawable drawable) { 541 | Window rroot = None; 542 | int x = 0, y = 0; 543 | unsigned width = 0, height = 0, border = 0, depth = 0; 544 | if (XGetGeometry(ps->dpy, drawable, &rroot, &x, &y, &width, &height, 545 | &border, &depth)) { 546 | printf_dbgf("(%#010lx): x = %u, y = %u, wid = %u, hei = %d, b = %u, d = %u\n", drawable, x, y, width, height, border, depth); 547 | } 548 | else { 549 | printf_dbgf("(%#010lx): Failed\n", drawable); 550 | } 551 | } 552 | 553 | static void 554 | win_rounded_corners(session_t *ps, win *w); 555 | 556 | /** 557 | * Validate a pixmap. 558 | * 559 | * Detect whether the pixmap is valid with XGetGeometry. Well, maybe there 560 | * are better ways. 561 | */ 562 | static inline bool 563 | validate_pixmap(session_t *ps, Pixmap pxmap) { 564 | if (!pxmap) return false; 565 | 566 | Window rroot = None; 567 | int rx = 0, ry = 0; 568 | unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0; 569 | return XGetGeometry(ps->dpy, pxmap, &rroot, &rx, &ry, 570 | &rwid, &rhei, &rborder, &rdepth) && rwid && rhei; 571 | } 572 | 573 | /** 574 | * Validate pixmap of a window, and destroy pixmap and picture if invalid. 575 | */ 576 | static inline void 577 | win_validate_pixmap(session_t *ps, win *w) { 578 | // Destroy pixmap and picture, if invalid 579 | if (!validate_pixmap(ps, w->paint.pixmap)) 580 | free_paint(ps, &w->paint); 581 | } 582 | 583 | /** 584 | * Wrapper of c2_match(). 585 | */ 586 | static inline bool 587 | win_match(session_t *ps, win *w, c2_lptr_t *condlst, const c2_lptr_t **cache) { 588 | #ifdef CONFIG_C2 589 | return c2_match(ps, w, condlst, cache); 590 | #else 591 | return false; 592 | #endif 593 | } 594 | 595 | static bool 596 | condlst_add(session_t *ps, c2_lptr_t **pcondlst, const char *pattern); 597 | 598 | static long 599 | determine_evmask(session_t *ps, Window wid, win_evmode_t mode); 600 | 601 | /** 602 | * Clear leader cache of all windows. 603 | */ 604 | static void 605 | clear_cache_win_leaders(session_t *ps) { 606 | for (win *w = ps->list; w; w = w->next) 607 | w->cache_leader = None; 608 | } 609 | 610 | static win * 611 | find_toplevel2(session_t *ps, Window wid); 612 | 613 | /** 614 | * Find matched window. 615 | */ 616 | static inline win * 617 | find_win_all(session_t *ps, const Window wid) { 618 | if (!wid || PointerRoot == wid || wid == ps->root || wid == ps->overlay) 619 | return NULL; 620 | 621 | win *w = find_win(ps, wid); 622 | if (!w) w = find_toplevel(ps, wid); 623 | if (!w) w = find_toplevel2(ps, wid); 624 | return w; 625 | } 626 | 627 | static Window 628 | win_get_leader_raw(session_t *ps, win *w, int recursions); 629 | 630 | /** 631 | * Get the leader of a window. 632 | * 633 | * This function updates w->cache_leader if necessary. 634 | */ 635 | static inline Window 636 | win_get_leader(session_t *ps, win *w) { 637 | return win_get_leader_raw(ps, w, 0); 638 | } 639 | 640 | /** 641 | * Return whether a window group is really focused. 642 | * 643 | * @param leader leader window ID 644 | * @return true if the window group is focused, false otherwise 645 | */ 646 | static inline bool 647 | group_is_focused(session_t *ps, Window leader) { 648 | if (!leader) 649 | return false; 650 | 651 | for (win *w = ps->list; w; w = w->next) { 652 | if (win_get_leader(ps, w) == leader && !w->destroyed 653 | && win_is_focused_real(ps, w)) 654 | return true; 655 | } 656 | 657 | return false; 658 | } 659 | 660 | static win * 661 | recheck_focus(session_t *ps); 662 | 663 | static bool 664 | get_root_tile(session_t *ps); 665 | 666 | static void 667 | paint_root(session_t *ps, XserverRegion reg_paint); 668 | 669 | static XserverRegion 670 | win_get_region(session_t *ps, win *w, bool use_offset); 671 | 672 | static XserverRegion 673 | win_get_region_noframe(session_t *ps, win *w, bool use_offset); 674 | 675 | static XserverRegion 676 | win_extents(session_t *ps, win *w); 677 | 678 | static XserverRegion 679 | border_size(session_t *ps, win *w, bool use_offset); 680 | 681 | static Window 682 | find_client_win(session_t *ps, Window w); 683 | 684 | static void 685 | get_frame_extents(session_t *ps, win *w, Window client); 686 | 687 | static win * 688 | paint_preprocess(session_t *ps, win *list); 689 | 690 | static void 691 | render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, 692 | double opacity, bool argb, bool neg, 693 | Picture pict, glx_texture_t *ptex, 694 | XserverRegion reg_paint, const reg_data_t *pcache_reg 695 | #ifdef CONFIG_VSYNC_OPENGL_GLSL 696 | , const glx_prog_main_t *pprogram 697 | #endif 698 | ); 699 | 700 | #ifdef CONFIG_VSYNC_OPENGL_GLSL 701 | #define \ 702 | render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ 703 | render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) 704 | #else 705 | #define \ 706 | render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ 707 | render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg) 708 | #endif 709 | 710 | static inline void 711 | win_render(session_t *ps, win *w, int x, int y, int wid, int hei, 712 | double opacity, XserverRegion reg_paint, const reg_data_t *pcache_reg, 713 | Picture pict) { 714 | const int dx = (w ? w->a.x: 0) + x; 715 | const int dy = (w ? w->a.y: 0) + y; 716 | const bool argb = (w && (WMODE_ARGB == w->mode || ps->o.force_win_blend)); 717 | const bool neg = (w && w->invert_color); 718 | 719 | render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, 720 | pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), 721 | reg_paint, pcache_reg, (w ? &ps->o.glx_prog_win: NULL)); 722 | } 723 | 724 | static inline void 725 | set_tgt_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { 726 | switch (ps->o.backend) { 727 | case BKEND_XRENDER: 728 | case BKEND_XR_GLX_HYBRID: 729 | XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer.pict, 0, 0, reg); 730 | break; 731 | #ifdef CONFIG_VSYNC_OPENGL 732 | case BKEND_GLX: 733 | glx_set_clip(ps, reg, pcache_reg); 734 | break; 735 | #endif 736 | } 737 | } 738 | 739 | static bool 740 | xr_blur_dst(session_t *ps, Picture tgt_buffer, 741 | int x, int y, int wid, int hei, XFixed **blur_kerns, 742 | XserverRegion reg_clip); 743 | 744 | /** 745 | * Normalize a convolution kernel. 746 | */ 747 | static inline void 748 | normalize_conv_kern(int wid, int hei, XFixed *kern) { 749 | double sum = 0.0; 750 | for (int i = 0; i < wid * hei; ++i) 751 | sum += XFixedToDouble(kern[i]); 752 | double factor = 1.0 / sum; 753 | for (int i = 0; i < wid * hei; ++i) 754 | kern[i] = XDoubleToFixed(XFixedToDouble(kern[i]) * factor); 755 | } 756 | 757 | static void 758 | paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t); 759 | 760 | static void 761 | add_damage(session_t *ps, XserverRegion damage); 762 | 763 | static void 764 | repair_win(session_t *ps, win *w); 765 | 766 | static wintype_t 767 | wid_get_prop_wintype(session_t *ps, Window w); 768 | 769 | static void 770 | map_win(session_t *ps, Window id); 771 | 772 | static void 773 | finish_map_win(session_t *ps, win *w); 774 | 775 | static void 776 | finish_unmap_win(session_t *ps, win *w); 777 | 778 | static void 779 | unmap_callback(session_t *ps, win *w); 780 | 781 | static void 782 | unmap_win(session_t *ps, win *w); 783 | 784 | static opacity_t 785 | wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def); 786 | 787 | /** 788 | * Reread opacity property of a window. 789 | */ 790 | static inline void 791 | win_update_opacity_prop(session_t *ps, win *w) { 792 | w->opacity_prop = wid_get_opacity_prop(ps, w->id, OPAQUE); 793 | if (!ps->o.detect_client_opacity || !w->client_win 794 | || w->id == w->client_win) 795 | w->opacity_prop_client = OPAQUE; 796 | else 797 | w->opacity_prop_client = wid_get_opacity_prop(ps, w->client_win, 798 | OPAQUE); 799 | } 800 | 801 | static double 802 | get_opacity_percent(win *w); 803 | 804 | static void 805 | win_determine_mode(session_t *ps, win *w); 806 | 807 | static void 808 | calc_opacity(session_t *ps, win *w); 809 | 810 | static void 811 | calc_dim(session_t *ps, win *w); 812 | 813 | static Window 814 | wid_get_prop_window(session_t *ps, Window wid, Atom aprop); 815 | 816 | static void 817 | win_update_leader(session_t *ps, win *w); 818 | 819 | static void 820 | win_set_leader(session_t *ps, win *w, Window leader); 821 | 822 | static void 823 | win_update_focused(session_t *ps, win *w); 824 | 825 | /** 826 | * Run win_update_focused() on all windows with the same leader window. 827 | * 828 | * @param leader leader window ID 829 | */ 830 | static inline void 831 | group_update_focused(session_t *ps, Window leader) { 832 | if (!leader) 833 | return; 834 | 835 | for (win *w = ps->list; w; w = w->next) { 836 | if (win_get_leader(ps, w) == leader && !w->destroyed) 837 | win_update_focused(ps, w); 838 | } 839 | 840 | return; 841 | } 842 | 843 | static inline void 844 | win_set_focused(session_t *ps, win *w, bool focused); 845 | 846 | static void 847 | win_on_focus_change(session_t *ps, win *w); 848 | 849 | static void 850 | win_determine_fade(session_t *ps, win *w); 851 | 852 | static void 853 | win_update_shape_raw(session_t *ps, win *w); 854 | 855 | static void 856 | win_update_shape(session_t *ps, win *w); 857 | 858 | static void 859 | win_update_prop_shadow_raw(session_t *ps, win *w); 860 | 861 | static void 862 | win_update_prop_shadow(session_t *ps, win *w); 863 | 864 | static void 865 | win_set_shadow(session_t *ps, win *w, bool shadow_new); 866 | 867 | static void 868 | win_determine_shadow(session_t *ps, win *w); 869 | 870 | static void 871 | win_set_invert_color(session_t *ps, win *w, bool invert_color_new); 872 | 873 | static void 874 | win_determine_invert_color(session_t *ps, win *w); 875 | 876 | static void 877 | win_set_blur_background(session_t *ps, win *w, bool blur_background_new); 878 | 879 | static void 880 | win_determine_blur_background(session_t *ps, win *w); 881 | 882 | static void 883 | win_on_wtype_change(session_t *ps, win *w); 884 | 885 | static void 886 | win_on_factor_change(session_t *ps, win *w); 887 | 888 | static void 889 | win_upd_run(session_t *ps, win *w, win_upd_t *pupd); 890 | 891 | static void 892 | calc_win_size(session_t *ps, win *w); 893 | 894 | static void 895 | calc_shadow_geometry(session_t *ps, win *w); 896 | 897 | static void 898 | win_upd_wintype(session_t *ps, win *w); 899 | 900 | static void 901 | win_mark_client(session_t *ps, win *w, Window client); 902 | 903 | static void 904 | win_unmark_client(session_t *ps, win *w); 905 | 906 | static void 907 | win_recheck_client(session_t *ps, win *w); 908 | 909 | static bool 910 | add_win(session_t *ps, Window id, Window prev); 911 | 912 | static void 913 | restack_win(session_t *ps, win *w, Window new_above); 914 | 915 | static void 916 | configure_win(session_t *ps, XConfigureEvent *ce); 917 | 918 | static void 919 | circulate_win(session_t *ps, XCirculateEvent *ce); 920 | 921 | static void 922 | finish_destroy_win(session_t *ps, Window id); 923 | 924 | static void 925 | destroy_callback(session_t *ps, win *w); 926 | 927 | static void 928 | destroy_win(session_t *ps, Window id); 929 | 930 | static void 931 | damage_win(session_t *ps, XDamageNotifyEvent *de); 932 | 933 | static int 934 | xerror(Display *dpy, XErrorEvent *ev); 935 | 936 | static void 937 | expose_root(session_t *ps, XRectangle *rects, int nrects); 938 | 939 | static Window 940 | wid_get_prop_window(session_t *ps, Window wid, Atom aprop); 941 | 942 | static bool 943 | wid_get_name(session_t *ps, Window w, char **name); 944 | 945 | static bool 946 | wid_get_role(session_t *ps, Window w, char **role); 947 | 948 | static int 949 | win_get_prop_str(session_t *ps, win *w, char **tgt, 950 | bool (*func_wid_get_prop_str)(session_t *ps, Window wid, char **tgt)); 951 | 952 | static inline int 953 | win_get_name(session_t *ps, win *w) { 954 | int ret = win_get_prop_str(ps, w, &w->name, wid_get_name); 955 | 956 | #ifdef DEBUG_WINDATA 957 | printf_dbgf("(%#010lx): client = %#010lx, name = \"%s\", " 958 | "ret = %d\n", w->id, w->client_win, w->name, ret); 959 | #endif 960 | 961 | return ret; 962 | } 963 | 964 | static inline int 965 | win_get_role(session_t *ps, win *w) { 966 | int ret = win_get_prop_str(ps, w, &w->role, wid_get_role); 967 | 968 | #ifdef DEBUG_WINDATA 969 | printf_dbgf("(%#010lx): client = %#010lx, role = \"%s\", " 970 | "ret = %d\n", w->id, w->client_win, w->role, ret); 971 | #endif 972 | 973 | return ret; 974 | } 975 | 976 | static bool 977 | win_get_class(session_t *ps, win *w); 978 | 979 | #ifdef DEBUG_EVENTS 980 | static int 981 | ev_serial(XEvent *ev); 982 | 983 | static const char * 984 | ev_name(session_t *ps, XEvent *ev); 985 | 986 | static Window 987 | ev_window(session_t *ps, XEvent *ev); 988 | #endif 989 | 990 | static void __attribute__ ((noreturn)) 991 | usage(int ret); 992 | 993 | static bool 994 | register_cm(session_t *ps); 995 | 996 | inline static void 997 | ev_focus_in(session_t *ps, XFocusChangeEvent *ev); 998 | 999 | inline static void 1000 | ev_focus_out(session_t *ps, XFocusChangeEvent *ev); 1001 | 1002 | inline static void 1003 | ev_create_notify(session_t *ps, XCreateWindowEvent *ev); 1004 | 1005 | inline static void 1006 | ev_configure_notify(session_t *ps, XConfigureEvent *ev); 1007 | 1008 | inline static void 1009 | ev_destroy_notify(session_t *ps, XDestroyWindowEvent *ev); 1010 | 1011 | inline static void 1012 | ev_map_notify(session_t *ps, XMapEvent *ev); 1013 | 1014 | inline static void 1015 | ev_unmap_notify(session_t *ps, XUnmapEvent *ev); 1016 | 1017 | inline static void 1018 | ev_reparent_notify(session_t *ps, XReparentEvent *ev); 1019 | 1020 | inline static void 1021 | ev_circulate_notify(session_t *ps, XCirculateEvent *ev); 1022 | 1023 | inline static void 1024 | ev_expose(session_t *ps, XExposeEvent *ev); 1025 | 1026 | static void 1027 | update_ewmh_active_win(session_t *ps); 1028 | 1029 | inline static void 1030 | ev_property_notify(session_t *ps, XPropertyEvent *ev); 1031 | 1032 | inline static void 1033 | ev_damage_notify(session_t *ps, XDamageNotifyEvent *ev); 1034 | 1035 | inline static void 1036 | ev_shape_notify(session_t *ps, XShapeEvent *ev); 1037 | 1038 | /** 1039 | * Get a region of the screen size. 1040 | */ 1041 | inline static XserverRegion 1042 | get_screen_region(session_t *ps) { 1043 | XRectangle r; 1044 | 1045 | r.x = 0; 1046 | r.y = 0; 1047 | r.width = ps->root_width; 1048 | r.height = ps->root_height; 1049 | return XFixesCreateRegion(ps->dpy, &r, 1); 1050 | } 1051 | 1052 | /** 1053 | * Resize a region. 1054 | */ 1055 | static inline void 1056 | resize_region(session_t *ps, XserverRegion region, short mod) { 1057 | if (!mod || !region) return; 1058 | 1059 | int nrects = 0, nnewrects = 0; 1060 | XRectangle *newrects = NULL; 1061 | XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); 1062 | if (!rects || !nrects) 1063 | goto resize_region_end; 1064 | 1065 | // Allocate memory for new rectangle list, because I don't know if it's 1066 | // safe to write in the memory Xlib allocates 1067 | newrects = calloc(nrects, sizeof(XRectangle)); 1068 | if (!newrects) { 1069 | printf_errf("(): Failed to allocate memory."); 1070 | exit(1); 1071 | } 1072 | 1073 | // Loop through all rectangles 1074 | for (int i = 0; i < nrects; ++i) { 1075 | int x1 = max_i(rects[i].x - mod, 0); 1076 | int y1 = max_i(rects[i].y - mod, 0); 1077 | int x2 = min_i(rects[i].x + rects[i].width + mod, ps->root_width); 1078 | int y2 = min_i(rects[i].y + rects[i].height + mod, ps->root_height); 1079 | int wid = x2 - x1; 1080 | int hei = y2 - y1; 1081 | if (wid <= 0 || hei <= 0) 1082 | continue; 1083 | newrects[nnewrects].x = x1; 1084 | newrects[nnewrects].y = y1; 1085 | newrects[nnewrects].width = wid; 1086 | newrects[nnewrects].height = hei; 1087 | ++nnewrects; 1088 | } 1089 | 1090 | // Set region 1091 | XFixesSetRegion(ps->dpy, region, newrects, nnewrects); 1092 | 1093 | resize_region_end: 1094 | cxfree(rects); 1095 | free(newrects); 1096 | } 1097 | 1098 | /** 1099 | * Dump a region. 1100 | */ 1101 | static inline void 1102 | dump_region(const session_t *ps, XserverRegion region) { 1103 | int nrects = 0; 1104 | XRectangle *rects = NULL; 1105 | if (!rects && region) 1106 | rects = XFixesFetchRegion(ps->dpy, region, &nrects); 1107 | 1108 | printf_dbgf("(%#010lx): %d rects\n", region, nrects); 1109 | if (!rects) return; 1110 | for (int i = 0; i < nrects; ++i) 1111 | printf("Rect #%d: %8d, %8d, %8d, %8d\n", i, rects[i].x, rects[i].y, 1112 | rects[i].width, rects[i].height); 1113 | putchar('\n'); 1114 | fflush(stdout); 1115 | 1116 | cxfree(rects); 1117 | } 1118 | 1119 | /** 1120 | * Check if a region is empty. 1121 | * 1122 | * Keith Packard said this is slow: 1123 | * http://lists.freedesktop.org/archives/xorg/2007-November/030467.html 1124 | * 1125 | * @param ps current session 1126 | * @param region region to check for 1127 | * @param pcache_rects a place to cache the dumped rectangles 1128 | * @param ncache_nrects a place to cache the number of dumped rectangles 1129 | */ 1130 | static inline bool 1131 | is_region_empty(const session_t *ps, XserverRegion region, 1132 | reg_data_t *pcache_reg) { 1133 | int nrects = 0; 1134 | XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); 1135 | 1136 | if (pcache_reg) { 1137 | pcache_reg->rects = rects; 1138 | pcache_reg->nrects = nrects; 1139 | } 1140 | else 1141 | cxfree(rects); 1142 | 1143 | return !nrects; 1144 | } 1145 | 1146 | /** 1147 | * Add a window to damaged area. 1148 | * 1149 | * @param ps current session 1150 | * @param w struct _win element representing the window 1151 | */ 1152 | static inline void 1153 | add_damage_win(session_t *ps, win *w) { 1154 | if (w->extents) { 1155 | add_damage(ps, copy_region(ps, w->extents)); 1156 | } 1157 | } 1158 | 1159 | #if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) 1160 | static bool 1161 | ev_window_name(session_t *ps, Window wid, char **name); 1162 | #endif 1163 | 1164 | inline static void 1165 | ev_handle(session_t *ps, XEvent *ev); 1166 | 1167 | static bool 1168 | fork_after(session_t *ps); 1169 | 1170 | #ifdef CONFIG_LIBCONFIG 1171 | /** 1172 | * Wrapper of libconfig's config_lookup_int. 1173 | * 1174 | * To convert int value config_lookup_bool 1175 | * returns to bool. 1176 | */ 1177 | static inline void 1178 | lcfg_lookup_bool(const config_t *config, const char *path, 1179 | bool *value) { 1180 | int ival; 1181 | 1182 | if (config_lookup_bool(config, path, &ival)) 1183 | *value = ival; 1184 | } 1185 | 1186 | /** 1187 | * Wrapper of libconfig's config_lookup_int. 1188 | * 1189 | * To deal with the different value types config_lookup_int 1190 | * returns in libconfig-1.3 and libconfig-1.4. 1191 | */ 1192 | static inline int 1193 | lcfg_lookup_int(const config_t *config, const char *path, int *value) { 1194 | #ifndef CONFIG_LIBCONFIG_LEGACY 1195 | return config_lookup_int(config, path, value); 1196 | #else 1197 | long lval; 1198 | int ret; 1199 | 1200 | if ((ret = config_lookup_int(config, path, &lval))) 1201 | *value = lval; 1202 | 1203 | return ret; 1204 | #endif 1205 | } 1206 | 1207 | static FILE * 1208 | open_config_file(char *cpath, char **path); 1209 | 1210 | static void 1211 | parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst, 1212 | const char *name); 1213 | 1214 | static void 1215 | parse_config(session_t *ps, struct options_tmp *pcfgtmp); 1216 | #endif 1217 | 1218 | static void 1219 | get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass); 1220 | 1221 | static void 1222 | init_atoms(session_t *ps); 1223 | 1224 | static void 1225 | update_refresh_rate(session_t *ps); 1226 | 1227 | static bool 1228 | swopti_init(session_t *ps); 1229 | 1230 | static void 1231 | swopti_handle_timeout(session_t *ps, struct timeval *ptv); 1232 | 1233 | #ifdef CONFIG_VSYNC_OPENGL 1234 | /** 1235 | * Ensure we have a GLX context. 1236 | */ 1237 | static inline bool 1238 | ensure_glx_context(session_t *ps) { 1239 | // Create GLX context 1240 | if (!glx_has_context(ps)) 1241 | glx_init(ps, false); 1242 | 1243 | return ps->psglx->context; 1244 | } 1245 | #endif 1246 | 1247 | static bool 1248 | vsync_drm_init(session_t *ps); 1249 | 1250 | #ifdef CONFIG_VSYNC_DRM 1251 | static int 1252 | vsync_drm_wait(session_t *ps); 1253 | #endif 1254 | 1255 | static bool 1256 | vsync_opengl_init(session_t *ps); 1257 | 1258 | static bool 1259 | vsync_opengl_oml_init(session_t *ps); 1260 | 1261 | static bool 1262 | vsync_opengl_swc_init(session_t *ps); 1263 | 1264 | static bool 1265 | vsync_opengl_mswc_init(session_t *ps); 1266 | 1267 | #ifdef CONFIG_VSYNC_OPENGL 1268 | static int 1269 | vsync_opengl_wait(session_t *ps); 1270 | 1271 | static int 1272 | vsync_opengl_oml_wait(session_t *ps); 1273 | 1274 | static void 1275 | vsync_opengl_swc_deinit(session_t *ps); 1276 | 1277 | static void 1278 | vsync_opengl_mswc_deinit(session_t *ps); 1279 | #endif 1280 | 1281 | static void 1282 | vsync_wait(session_t *ps); 1283 | 1284 | static void 1285 | init_alpha_picts(session_t *ps); 1286 | 1287 | static bool 1288 | init_dbe(session_t *ps); 1289 | 1290 | static bool 1291 | init_overlay(session_t *ps); 1292 | 1293 | static void 1294 | redir_start(session_t *ps); 1295 | 1296 | static void 1297 | redir_stop(session_t *ps); 1298 | 1299 | static inline time_ms_t 1300 | timeout_get_newrun(const timeout_t *ptmout) { 1301 | return ptmout->firstrun + (max_l((ptmout->lastrun + (time_ms_t) (ptmout->interval * TIMEOUT_RUN_TOLERANCE) - ptmout->firstrun) / ptmout->interval, (ptmout->lastrun + (time_ms_t) (ptmout->interval * (1 - TIMEOUT_RUN_TOLERANCE)) - ptmout->firstrun) / ptmout->interval) + 1) * ptmout->interval; 1302 | } 1303 | 1304 | static time_ms_t 1305 | timeout_get_poll_time(session_t *ps); 1306 | 1307 | static void 1308 | timeout_clear(session_t *ps); 1309 | 1310 | static bool 1311 | tmout_unredir_callback(session_t *ps, timeout_t *tmout); 1312 | 1313 | static bool 1314 | mainloop(session_t *ps); 1315 | 1316 | #ifdef CONFIG_XINERAMA 1317 | static void 1318 | cxinerama_upd_scrs(session_t *ps); 1319 | #endif 1320 | 1321 | /** 1322 | * Get the Xinerama screen a window is on. 1323 | * 1324 | * Return an index >= 0, or -1 if not found. 1325 | */ 1326 | static inline void 1327 | cxinerama_win_upd_scr(session_t *ps, win *w) { 1328 | #ifdef CONFIG_XINERAMA 1329 | w->xinerama_scr = -1; 1330 | for (XineramaScreenInfo *s = ps->xinerama_scrs; 1331 | s < ps->xinerama_scrs + ps->xinerama_nscrs; ++s) 1332 | if (s->x_org <= w->a.x && s->y_org <= w->a.y 1333 | && s->x_org + s->width >= w->a.x + w->widthb 1334 | && s->y_org + s->height >= w->a.y + w->heightb) { 1335 | w->xinerama_scr = s - ps->xinerama_scrs; 1336 | return; 1337 | } 1338 | #endif 1339 | } 1340 | 1341 | static void 1342 | cxinerama_upd_scrs(session_t *ps); 1343 | 1344 | static session_t * 1345 | session_init(session_t *ps_old, int argc, char **argv); 1346 | 1347 | static void 1348 | session_destroy(session_t *ps); 1349 | 1350 | static void 1351 | session_run(session_t *ps); 1352 | 1353 | static void 1354 | reset_enable(int __attribute__((unused)) signum); 1355 | -------------------------------------------------------------------------------- /src/dbus.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Compton - a compositor for X11 3 | * 4 | * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard 5 | * 6 | * Copyright (c) 2011-2013, Christopher Jeffrey 7 | * See LICENSE for more information. 8 | * 9 | */ 10 | 11 | #include "dbus.h" 12 | 13 | /** 14 | * Initialize D-Bus connection. 15 | */ 16 | bool 17 | cdbus_init(session_t *ps) { 18 | DBusError err = { }; 19 | 20 | // Initialize 21 | dbus_error_init(&err); 22 | 23 | // Connect to D-Bus 24 | // Use dbus_bus_get_private() so we can fully recycle it ourselves 25 | ps->dbus_conn = dbus_bus_get_private(DBUS_BUS_SESSION, &err); 26 | if (dbus_error_is_set(&err)) { 27 | printf_errf("(): D-Bus connection failed (%s).", err.message); 28 | dbus_error_free(&err); 29 | return false; 30 | } 31 | 32 | if (!ps->dbus_conn) { 33 | printf_errf("(): D-Bus connection failed for unknown reason."); 34 | return false; 35 | } 36 | 37 | // Avoid exiting on disconnect 38 | dbus_connection_set_exit_on_disconnect(ps->dbus_conn, false); 39 | 40 | // Request service name 41 | { 42 | // Build service name 43 | char *service = mstrjoin3(CDBUS_SERVICE_NAME, ".", ps->o.display_repr); 44 | ps->dbus_service = service; 45 | 46 | // Request for the name 47 | int ret = dbus_bus_request_name(ps->dbus_conn, service, 48 | DBUS_NAME_FLAG_DO_NOT_QUEUE, &err); 49 | 50 | if (dbus_error_is_set(&err)) { 51 | printf_errf("(): Failed to obtain D-Bus name (%s).", err.message); 52 | dbus_error_free(&err); 53 | } 54 | 55 | if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret 56 | && DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER != ret) { 57 | printf_errf("(): Failed to become the primary owner of requested " 58 | "D-Bus name (%d).", ret); 59 | } 60 | } 61 | 62 | 63 | // Add watch handlers 64 | if (!dbus_connection_set_watch_functions(ps->dbus_conn, 65 | cdbus_callback_add_watch, cdbus_callback_remove_watch, 66 | cdbus_callback_watch_toggled, ps, NULL)) { 67 | printf_errf("(): Failed to add D-Bus watch functions."); 68 | return false; 69 | } 70 | 71 | // Add timeout handlers 72 | if (!dbus_connection_set_timeout_functions(ps->dbus_conn, 73 | cdbus_callback_add_timeout, cdbus_callback_remove_timeout, 74 | cdbus_callback_timeout_toggled, ps, NULL)) { 75 | printf_errf("(): Failed to add D-Bus timeout functions."); 76 | return false; 77 | } 78 | 79 | // Add match 80 | dbus_bus_add_match(ps->dbus_conn, 81 | "type='method_call',interface='" CDBUS_INTERFACE_NAME "'", &err); 82 | if (dbus_error_is_set(&err)) { 83 | printf_errf("(): Failed to add D-Bus match."); 84 | dbus_error_free(&err); 85 | return false; 86 | } 87 | 88 | return true; 89 | } 90 | 91 | /** 92 | * Destroy D-Bus connection. 93 | */ 94 | void 95 | cdbus_destroy(session_t *ps) { 96 | if (ps->dbus_conn) { 97 | // Release DBus name firstly 98 | if (ps->dbus_service) { 99 | DBusError err = { }; 100 | dbus_error_init(&err); 101 | 102 | dbus_bus_release_name(ps->dbus_conn, ps->dbus_service, &err); 103 | if (dbus_error_is_set(&err)) { 104 | printf_errf("(): Failed to release DBus name (%s).", 105 | err.message); 106 | dbus_error_free(&err); 107 | } 108 | } 109 | 110 | // Close and unref the connection 111 | dbus_connection_close(ps->dbus_conn); 112 | dbus_connection_unref(ps->dbus_conn); 113 | } 114 | } 115 | 116 | /** @name DBusTimeout handling 117 | */ 118 | ///@{ 119 | 120 | /** 121 | * Callback for adding D-Bus timeout. 122 | */ 123 | static dbus_bool_t 124 | cdbus_callback_add_timeout(DBusTimeout *timeout, void *data) { 125 | session_t *ps = data; 126 | 127 | timeout_t *ptmout = timeout_insert(ps, dbus_timeout_get_interval(timeout), 128 | cdbus_callback_handle_timeout, timeout); 129 | if (ptmout) 130 | dbus_timeout_set_data(timeout, ptmout, NULL); 131 | 132 | return (bool) ptmout; 133 | } 134 | 135 | /** 136 | * Callback for removing D-Bus timeout. 137 | */ 138 | static void 139 | cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data) { 140 | session_t *ps = data; 141 | 142 | timeout_t *ptmout = dbus_timeout_get_data(timeout); 143 | assert(ptmout); 144 | if (ptmout) 145 | timeout_drop(ps, ptmout); 146 | } 147 | 148 | /** 149 | * Callback for toggling a D-Bus timeout. 150 | */ 151 | static void 152 | cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data) { 153 | timeout_t *ptmout = dbus_timeout_get_data(timeout); 154 | 155 | assert(ptmout); 156 | if (ptmout) { 157 | ptmout->enabled = dbus_timeout_get_enabled(timeout); 158 | // Refresh interval as libdbus doc says: "Whenever a timeout is toggled, 159 | // its interval may change." 160 | ptmout->interval = dbus_timeout_get_interval(timeout); 161 | } 162 | } 163 | 164 | /** 165 | * Callback for handling a D-Bus timeout. 166 | */ 167 | static bool 168 | cdbus_callback_handle_timeout(session_t *ps, timeout_t *ptmout) { 169 | assert(ptmout && ptmout->data); 170 | if (ptmout && ptmout->data) 171 | return dbus_timeout_handle(ptmout->data); 172 | 173 | return false; 174 | } 175 | 176 | ///@} 177 | 178 | /** @name DBusWatch handling 179 | */ 180 | ///@{ 181 | 182 | /** 183 | * Callback for adding D-Bus watch. 184 | */ 185 | static dbus_bool_t 186 | cdbus_callback_add_watch(DBusWatch *watch, void *data) { 187 | // Leave disabled watches alone 188 | if (!dbus_watch_get_enabled(watch)) 189 | return TRUE; 190 | 191 | session_t *ps = data; 192 | 193 | // Insert the file descriptor 194 | fds_insert(ps, dbus_watch_get_unix_fd(watch), 195 | cdbus_get_watch_cond(watch)); 196 | 197 | // Always return true 198 | return TRUE; 199 | } 200 | 201 | /** 202 | * Callback for removing D-Bus watch. 203 | */ 204 | static void 205 | cdbus_callback_remove_watch(DBusWatch *watch, void *data) { 206 | session_t *ps = data; 207 | 208 | fds_drop(ps, dbus_watch_get_unix_fd(watch), 209 | cdbus_get_watch_cond(watch)); 210 | } 211 | 212 | /** 213 | * Callback for toggling D-Bus watch status. 214 | */ 215 | static void 216 | cdbus_callback_watch_toggled(DBusWatch *watch, void *data) { 217 | if (dbus_watch_get_enabled(watch)) { 218 | cdbus_callback_add_watch(watch, data); 219 | } 220 | else { 221 | cdbus_callback_remove_watch(watch, data); 222 | } 223 | } 224 | 225 | ///@} 226 | 227 | /** @name Message argument appending callbacks 228 | */ 229 | ///@{ 230 | 231 | /** 232 | * Callback to append a bool argument to a message. 233 | */ 234 | static bool 235 | cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data) { 236 | assert(data); 237 | 238 | dbus_bool_t val = *(const bool *) data; 239 | 240 | if (!dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &val, 241 | DBUS_TYPE_INVALID)) { 242 | printf_errf("(): Failed to append argument."); 243 | return false; 244 | } 245 | 246 | return true; 247 | } 248 | 249 | /** 250 | * Callback to append an int32 argument to a message. 251 | */ 252 | static bool 253 | cdbus_apdarg_int32(session_t *ps, DBusMessage *msg, const void *data) { 254 | if (!dbus_message_append_args(msg, DBUS_TYPE_INT32, data, 255 | DBUS_TYPE_INVALID)) { 256 | printf_errf("(): Failed to append argument."); 257 | return false; 258 | } 259 | 260 | return true; 261 | } 262 | 263 | /** 264 | * Callback to append an uint32 argument to a message. 265 | */ 266 | static bool 267 | cdbus_apdarg_uint32(session_t *ps, DBusMessage *msg, const void *data) { 268 | if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, data, 269 | DBUS_TYPE_INVALID)) { 270 | printf_errf("(): Failed to append argument."); 271 | return false; 272 | } 273 | 274 | return true; 275 | } 276 | 277 | /** 278 | * Callback to append a double argument to a message. 279 | */ 280 | static bool 281 | cdbus_apdarg_double(session_t *ps, DBusMessage *msg, const void *data) { 282 | if (!dbus_message_append_args(msg, DBUS_TYPE_DOUBLE, data, 283 | DBUS_TYPE_INVALID)) { 284 | printf_errf("(): Failed to append argument."); 285 | return false; 286 | } 287 | 288 | return true; 289 | } 290 | 291 | /** 292 | * Callback to append a Window argument to a message. 293 | */ 294 | static bool 295 | cdbus_apdarg_wid(session_t *ps, DBusMessage *msg, const void *data) { 296 | assert(data); 297 | cdbus_window_t val = *(const Window *)data; 298 | 299 | if (!dbus_message_append_args(msg, CDBUS_TYPE_WINDOW, &val, 300 | DBUS_TYPE_INVALID)) { 301 | printf_errf("(): Failed to append argument."); 302 | return false; 303 | } 304 | 305 | return true; 306 | } 307 | 308 | /** 309 | * Callback to append an cdbus_enum_t argument to a message. 310 | */ 311 | static bool 312 | cdbus_apdarg_enum(session_t *ps, DBusMessage *msg, const void *data) { 313 | assert(data); 314 | if (!dbus_message_append_args(msg, CDBUS_TYPE_ENUM, data, 315 | DBUS_TYPE_INVALID)) { 316 | printf_errf("(): Failed to append argument."); 317 | return false; 318 | } 319 | 320 | return true; 321 | } 322 | 323 | /** 324 | * Callback to append a string argument to a message. 325 | */ 326 | static bool 327 | cdbus_apdarg_string(session_t *ps, DBusMessage *msg, const void *data) { 328 | const char *str = data; 329 | if (!str) 330 | str = ""; 331 | 332 | if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &str, 333 | DBUS_TYPE_INVALID)) { 334 | printf_errf("(): Failed to append argument."); 335 | return false; 336 | } 337 | 338 | return true; 339 | } 340 | 341 | /** 342 | * Callback to append all window IDs to a message. 343 | */ 344 | static bool 345 | cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data) { 346 | // Get the number of wids we are to include 347 | unsigned count = 0; 348 | for (win *w = ps->list; w; w = w->next) { 349 | if (!w->destroyed) 350 | ++count; 351 | } 352 | 353 | // Allocate memory for an array of window IDs 354 | cdbus_window_t *arr = malloc(sizeof(cdbus_window_t) * count); 355 | if (!arr) { 356 | printf_errf("(): Failed to allocate memory for window ID array."); 357 | return false; 358 | } 359 | 360 | // Build the array 361 | { 362 | cdbus_window_t *pcur = arr; 363 | for (win *w = ps->list; w; w = w->next) { 364 | if (!w->destroyed) { 365 | *pcur = w->id; 366 | ++pcur; 367 | assert(pcur <= arr + count); 368 | } 369 | } 370 | assert(pcur == arr + count); 371 | } 372 | 373 | // Append arguments 374 | if (!dbus_message_append_args(msg, DBUS_TYPE_ARRAY, CDBUS_TYPE_WINDOW, 375 | &arr, count, DBUS_TYPE_INVALID)) { 376 | printf_errf("(): Failed to append argument."); 377 | free(arr); 378 | return false; 379 | } 380 | 381 | free(arr); 382 | return true; 383 | } 384 | ///@} 385 | 386 | /** 387 | * Send a D-Bus signal. 388 | * 389 | * @param ps current session 390 | * @param name signal name 391 | * @param func a function that modifies the built message, to, for example, 392 | * add an argument 393 | * @param data data pointer to pass to the function 394 | */ 395 | static bool 396 | cdbus_signal(session_t *ps, const char *name, 397 | bool (*func)(session_t *ps, DBusMessage *msg, const void *data), 398 | const void *data) { 399 | DBusMessage* msg = NULL; 400 | 401 | // Create a signal 402 | msg = dbus_message_new_signal(CDBUS_OBJECT_NAME, CDBUS_INTERFACE_NAME, 403 | name); 404 | if (!msg) { 405 | printf_errf("(): Failed to create D-Bus signal."); 406 | return false; 407 | } 408 | 409 | // Append arguments onto message 410 | if (func && !func(ps, msg, data)) { 411 | dbus_message_unref(msg); 412 | return false; 413 | } 414 | 415 | // Send the message and flush the connection 416 | if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) { 417 | printf_errf("(): Failed to send D-Bus signal."); 418 | dbus_message_unref(msg); 419 | return false; 420 | } 421 | dbus_connection_flush(ps->dbus_conn); 422 | 423 | // Free the message 424 | dbus_message_unref(msg); 425 | 426 | return true; 427 | } 428 | 429 | /** 430 | * Send a D-Bus reply. 431 | * 432 | * @param ps current session 433 | * @param srcmsg original message 434 | * @param func a function that modifies the built message, to, for example, 435 | * add an argument 436 | * @param data data pointer to pass to the function 437 | */ 438 | static bool 439 | cdbus_reply(session_t *ps, DBusMessage *srcmsg, 440 | bool (*func)(session_t *ps, DBusMessage *msg, const void *data), 441 | const void *data) { 442 | DBusMessage* msg = NULL; 443 | 444 | // Create a reply 445 | msg = dbus_message_new_method_return(srcmsg); 446 | if (!msg) { 447 | printf_errf("(): Failed to create D-Bus reply."); 448 | return false; 449 | } 450 | 451 | // Append arguments onto message 452 | if (func && !func(ps, msg, data)) { 453 | dbus_message_unref(msg); 454 | return false; 455 | } 456 | 457 | // Send the message and flush the connection 458 | if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) { 459 | printf_errf("(): Failed to send D-Bus reply."); 460 | dbus_message_unref(msg); 461 | return false; 462 | } 463 | dbus_connection_flush(ps->dbus_conn); 464 | 465 | // Free the message 466 | dbus_message_unref(msg); 467 | 468 | return true; 469 | } 470 | 471 | /** 472 | * Send a D-Bus error reply. 473 | * 474 | * @param ps current session 475 | * @param msg the new error DBusMessage 476 | */ 477 | static bool 478 | cdbus_reply_errm(session_t *ps, DBusMessage *msg) { 479 | if (!msg) { 480 | printf_errf("(): Failed to create D-Bus reply."); 481 | return false; 482 | } 483 | 484 | // Send the message and flush the connection 485 | if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) { 486 | printf_errf("(): Failed to send D-Bus reply."); 487 | dbus_message_unref(msg); 488 | return false; 489 | } 490 | dbus_connection_flush(ps->dbus_conn); 491 | 492 | // Free the message 493 | dbus_message_unref(msg); 494 | 495 | return true; 496 | } 497 | 498 | /** 499 | * Get n-th argument of a D-Bus message. 500 | * 501 | * @param count the position of the argument to get, starting from 0 502 | * @param type libdbus type number of the type 503 | * @param pdest pointer to the target 504 | * @return true if successful, false otherwise. 505 | */ 506 | static bool 507 | cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest) { 508 | assert(count >= 0); 509 | 510 | DBusMessageIter iter = { }; 511 | if (!dbus_message_iter_init(msg, &iter)) { 512 | printf_errf("(): Message has no argument."); 513 | return false; 514 | } 515 | 516 | { 517 | const int oldcount = count; 518 | while (count) { 519 | if (!dbus_message_iter_next(&iter)) { 520 | printf_errf("(): Failed to find argument %d.", oldcount); 521 | return false; 522 | } 523 | --count; 524 | } 525 | } 526 | 527 | if (type != dbus_message_iter_get_arg_type(&iter)) { 528 | printf_errf("(): Argument has incorrect type."); 529 | return false; 530 | } 531 | 532 | dbus_message_iter_get_basic(&iter, pdest); 533 | 534 | return true; 535 | } 536 | 537 | void 538 | cdbus_loop(session_t *ps) { 539 | dbus_connection_read_write(ps->dbus_conn, 0); 540 | DBusMessage *msg = NULL; 541 | while ((msg = dbus_connection_pop_message(ps->dbus_conn))) 542 | cdbus_process(ps, msg); 543 | } 544 | 545 | /** @name Message processing 546 | */ 547 | ///@{ 548 | 549 | /** 550 | * Process a message from D-Bus. 551 | */ 552 | static void 553 | cdbus_process(session_t *ps, DBusMessage *msg) { 554 | bool success = false; 555 | 556 | #define cdbus_m_ismethod(method) \ 557 | dbus_message_is_method_call(msg, CDBUS_INTERFACE_NAME, method) 558 | 559 | if (cdbus_m_ismethod("reset")) { 560 | ps->reset = true; 561 | if (!dbus_message_get_no_reply(msg)) 562 | cdbus_reply_bool(ps, msg, true); 563 | success = true; 564 | } 565 | else if (cdbus_m_ismethod("repaint")) { 566 | force_repaint(ps); 567 | if (!dbus_message_get_no_reply(msg)) 568 | cdbus_reply_bool(ps, msg, true); 569 | success = true; 570 | } 571 | else if (cdbus_m_ismethod("list_win")) { 572 | success = cdbus_process_list_win(ps, msg); 573 | } 574 | else if (cdbus_m_ismethod("win_get")) { 575 | success = cdbus_process_win_get(ps, msg); 576 | } 577 | else if (cdbus_m_ismethod("win_set")) { 578 | success = cdbus_process_win_set(ps, msg); 579 | } 580 | else if (cdbus_m_ismethod("find_win")) { 581 | success = cdbus_process_find_win(ps, msg); 582 | } 583 | else if (cdbus_m_ismethod("opts_get")) { 584 | success = cdbus_process_opts_get(ps, msg); 585 | } 586 | else if (cdbus_m_ismethod("opts_set")) { 587 | success = cdbus_process_opts_set(ps, msg); 588 | } 589 | #undef cdbus_m_ismethod 590 | else if (dbus_message_is_method_call(msg, 591 | "org.freedesktop.DBus.Introspectable", "Introspect")) { 592 | success = cdbus_process_introspect(ps, msg); 593 | } 594 | else if (dbus_message_is_method_call(msg, 595 | "org.freedesktop.DBus.Peer", "Ping")) { 596 | cdbus_reply(ps, msg, NULL, NULL); 597 | success = true; 598 | } 599 | else if (dbus_message_is_method_call(msg, 600 | "org.freedesktop.DBus.Peer", "GetMachineId")) { 601 | char *uuid = dbus_get_local_machine_id(); 602 | if (uuid) { 603 | cdbus_reply_string(ps, msg, uuid); 604 | dbus_free(uuid); 605 | success = true; 606 | } 607 | } 608 | else if (dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameAcquired") 609 | || dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameLost")) { 610 | success = true; 611 | } 612 | else { 613 | if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(msg)) { 614 | printf_errf("(): Error message of path \"%s\" " 615 | "interface \"%s\", member \"%s\", error \"%s\"", 616 | dbus_message_get_path(msg), dbus_message_get_interface(msg), 617 | dbus_message_get_member(msg), dbus_message_get_error_name(msg)); 618 | } 619 | else { 620 | printf_errf("(): Illegal message of type \"%s\", path \"%s\" " 621 | "interface \"%s\", member \"%s\"", 622 | cdbus_repr_msgtype(msg), dbus_message_get_path(msg), 623 | dbus_message_get_interface(msg), dbus_message_get_member(msg)); 624 | } 625 | if (DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg) 626 | && !dbus_message_get_no_reply(msg)) 627 | cdbus_reply_err(ps, msg, CDBUS_ERROR_BADMSG, CDBUS_ERROR_BADMSG_S); 628 | success = true; 629 | } 630 | 631 | // If the message could not be processed, and an reply is expected, return 632 | // an empty reply. 633 | if (!success && DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg) 634 | && !dbus_message_get_no_reply(msg)) 635 | cdbus_reply_err(ps, msg, CDBUS_ERROR_UNKNOWN, CDBUS_ERROR_UNKNOWN_S); 636 | 637 | // Free the message 638 | dbus_message_unref(msg); 639 | } 640 | 641 | /** 642 | * Process a list_win D-Bus request. 643 | */ 644 | static bool 645 | cdbus_process_list_win(session_t *ps, DBusMessage *msg) { 646 | cdbus_reply(ps, msg, cdbus_apdarg_wids, NULL); 647 | 648 | return true; 649 | } 650 | 651 | /** 652 | * Process a win_get D-Bus request. 653 | */ 654 | static bool 655 | cdbus_process_win_get(session_t *ps, DBusMessage *msg) { 656 | cdbus_window_t wid = None; 657 | const char *target = NULL; 658 | DBusError err = { }; 659 | 660 | if (!dbus_message_get_args(msg, &err, 661 | CDBUS_TYPE_WINDOW, &wid, 662 | DBUS_TYPE_STRING, &target, 663 | DBUS_TYPE_INVALID)) { 664 | printf_errf("(): Failed to parse argument of \"win_get\" (%s).", 665 | err.message); 666 | dbus_error_free(&err); 667 | return false; 668 | } 669 | 670 | win *w = find_win(ps, wid); 671 | 672 | if (!w) { 673 | printf_errf("(): Window %#010x not found.", wid); 674 | cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid); 675 | return true; 676 | } 677 | 678 | #define cdbus_m_win_get_do(tgt, apdarg_func) \ 679 | if (!strcmp(MSTR(tgt), target)) { \ 680 | apdarg_func(ps, msg, w->tgt); \ 681 | return true; \ 682 | } 683 | 684 | cdbus_m_win_get_do(id, cdbus_reply_wid); 685 | 686 | // next 687 | if (!strcmp("next", target)) { 688 | cdbus_reply_wid(ps, msg, (w->next ? w->next->id: 0)); 689 | return true; 690 | } 691 | 692 | // map_state 693 | if (!strcmp("map_state", target)) { 694 | cdbus_reply_bool(ps, msg, w->a.map_state); 695 | return true; 696 | } 697 | 698 | cdbus_m_win_get_do(mode, cdbus_reply_enum); 699 | cdbus_m_win_get_do(client_win, cdbus_reply_wid); 700 | cdbus_m_win_get_do(damaged, cdbus_reply_bool); 701 | cdbus_m_win_get_do(destroyed, cdbus_reply_bool); 702 | cdbus_m_win_get_do(window_type, cdbus_reply_enum); 703 | cdbus_m_win_get_do(wmwin, cdbus_reply_bool); 704 | cdbus_m_win_get_do(leader, cdbus_reply_wid); 705 | // focused_real 706 | if (!strcmp("focused_real", target)) { 707 | cdbus_reply_bool(ps, msg, win_is_focused_real(ps, w)); 708 | return true; 709 | } 710 | cdbus_m_win_get_do(fade_force, cdbus_reply_enum); 711 | cdbus_m_win_get_do(shadow_force, cdbus_reply_enum); 712 | cdbus_m_win_get_do(focused_force, cdbus_reply_enum); 713 | cdbus_m_win_get_do(invert_color_force, cdbus_reply_enum); 714 | cdbus_m_win_get_do(name, cdbus_reply_string); 715 | cdbus_m_win_get_do(class_instance, cdbus_reply_string); 716 | cdbus_m_win_get_do(class_general, cdbus_reply_string); 717 | cdbus_m_win_get_do(role, cdbus_reply_string); 718 | 719 | cdbus_m_win_get_do(opacity, cdbus_reply_uint32); 720 | cdbus_m_win_get_do(opacity_tgt, cdbus_reply_uint32); 721 | cdbus_m_win_get_do(opacity_prop, cdbus_reply_uint32); 722 | cdbus_m_win_get_do(opacity_prop_client, cdbus_reply_uint32); 723 | cdbus_m_win_get_do(opacity_set, cdbus_reply_uint32); 724 | 725 | cdbus_m_win_get_do(frame_opacity, cdbus_reply_double); 726 | if (!strcmp("left_width", target)) { 727 | cdbus_reply_uint32(ps, msg, w->frame_extents.left); 728 | return true; 729 | } 730 | if (!strcmp("right_width", target)) { 731 | cdbus_reply_uint32(ps, msg, w->frame_extents.right); 732 | return true; 733 | } 734 | if (!strcmp("top_width", target)) { 735 | cdbus_reply_uint32(ps, msg, w->frame_extents.top); 736 | return true; 737 | } 738 | if (!strcmp("bottom_width", target)) { 739 | cdbus_reply_uint32(ps, msg, w->frame_extents.bottom); 740 | return true; 741 | } 742 | 743 | cdbus_m_win_get_do(shadow, cdbus_reply_bool); 744 | cdbus_m_win_get_do(fade, cdbus_reply_bool); 745 | cdbus_m_win_get_do(invert_color, cdbus_reply_bool); 746 | cdbus_m_win_get_do(blur_background, cdbus_reply_bool); 747 | #undef cdbus_m_win_get_do 748 | 749 | printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); 750 | cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); 751 | 752 | return true; 753 | } 754 | 755 | /** 756 | * Process a win_set D-Bus request. 757 | */ 758 | static bool 759 | cdbus_process_win_set(session_t *ps, DBusMessage *msg) { 760 | cdbus_window_t wid = None; 761 | const char *target = NULL; 762 | DBusError err = { }; 763 | 764 | if (!dbus_message_get_args(msg, &err, 765 | CDBUS_TYPE_WINDOW, &wid, 766 | DBUS_TYPE_STRING, &target, 767 | DBUS_TYPE_INVALID)) { 768 | printf_errf("(): Failed to parse argument of \"win_set\" (%s).", 769 | err.message); 770 | dbus_error_free(&err); 771 | return false; 772 | } 773 | 774 | win *w = find_win(ps, wid); 775 | 776 | if (!w) { 777 | printf_errf("(): Window %#010x not found.", wid); 778 | cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid); 779 | return true; 780 | } 781 | 782 | #define cdbus_m_win_set_do(tgt, type, real_type) \ 783 | if (!strcmp(MSTR(tgt), target)) { \ 784 | real_type val; \ 785 | if (!cdbus_msg_get_arg(msg, 2, type, &val)) \ 786 | return false; \ 787 | w->tgt = val; \ 788 | goto cdbus_process_win_set_success; \ 789 | } 790 | 791 | if (!strcmp("shadow_force", target)) { 792 | cdbus_enum_t val = UNSET; 793 | if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) 794 | return false; 795 | win_set_shadow_force(ps, w, val); 796 | goto cdbus_process_win_set_success; 797 | } 798 | 799 | if (!strcmp("fade_force", target)) { 800 | cdbus_enum_t val = UNSET; 801 | if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) 802 | return false; 803 | win_set_fade_force(ps, w, val); 804 | goto cdbus_process_win_set_success; 805 | } 806 | 807 | if (!strcmp("focused_force", target)) { 808 | cdbus_enum_t val = UNSET; 809 | if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) 810 | return false; 811 | win_set_focused_force(ps, w, val); 812 | goto cdbus_process_win_set_success; 813 | } 814 | 815 | if (!strcmp("invert_color_force", target)) { 816 | cdbus_enum_t val = UNSET; 817 | if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) 818 | return false; 819 | win_set_invert_color_force(ps, w, val); 820 | goto cdbus_process_win_set_success; 821 | } 822 | #undef cdbus_m_win_set_do 823 | 824 | printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); 825 | cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); 826 | 827 | return true; 828 | 829 | cdbus_process_win_set_success: 830 | if (!dbus_message_get_no_reply(msg)) 831 | cdbus_reply_bool(ps, msg, true); 832 | return true; 833 | } 834 | 835 | /** 836 | * Process a find_win D-Bus request. 837 | */ 838 | static bool 839 | cdbus_process_find_win(session_t *ps, DBusMessage *msg) { 840 | const char *target = NULL; 841 | 842 | if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) 843 | return false; 844 | 845 | Window wid = None; 846 | 847 | // Find window by client window 848 | if (!strcmp("client", target)) { 849 | cdbus_window_t client = None; 850 | if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_WINDOW, &client)) 851 | return false; 852 | win *w = find_toplevel(ps, client); 853 | if (w) 854 | wid = w->id; 855 | } 856 | // Find focused window 857 | else if (!strcmp("focused", target)) { 858 | win *w = find_focused(ps); 859 | if (w) 860 | wid = w->id; 861 | } 862 | else { 863 | printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); 864 | cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); 865 | 866 | return true; 867 | } 868 | 869 | cdbus_reply_wid(ps, msg, wid); 870 | 871 | return true; 872 | } 873 | 874 | /** 875 | * Process a opts_get D-Bus request. 876 | */ 877 | static bool 878 | cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { 879 | const char *target = NULL; 880 | 881 | if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) 882 | return false; 883 | 884 | #define cdbus_m_opts_get_do(tgt, apdarg_func) \ 885 | if (!strcmp(MSTR(tgt), target)) { \ 886 | apdarg_func(ps, msg, ps->o.tgt); \ 887 | return true; \ 888 | } 889 | 890 | // version 891 | if (!strcmp("version", target)) { 892 | cdbus_reply_string(ps, msg, COMPTON_VERSION); 893 | return true; 894 | } 895 | 896 | // pid 897 | if (!strcmp("pid", target)) { 898 | cdbus_reply_int32(ps, msg, getpid()); 899 | return true; 900 | } 901 | 902 | // display 903 | if (!strcmp("display", target)) { 904 | cdbus_reply_string(ps, msg, DisplayString(ps->dpy)); 905 | return true; 906 | } 907 | 908 | cdbus_m_opts_get_do(config_file, cdbus_reply_string); 909 | cdbus_m_opts_get_do(display_repr, cdbus_reply_string); 910 | cdbus_m_opts_get_do(write_pid_path, cdbus_reply_string); 911 | cdbus_m_opts_get_do(mark_wmwin_focused, cdbus_reply_bool); 912 | cdbus_m_opts_get_do(mark_ovredir_focused, cdbus_reply_bool); 913 | cdbus_m_opts_get_do(fork_after_register, cdbus_reply_bool); 914 | cdbus_m_opts_get_do(detect_rounded_corners, cdbus_reply_bool); 915 | cdbus_m_opts_get_do(paint_on_overlay, cdbus_reply_bool); 916 | // paint_on_overlay_id: Get ID of the X composite overlay window 917 | if (!strcmp("paint_on_overlay_id", target)) { 918 | cdbus_reply_uint32(ps, msg, ps->overlay); 919 | return true; 920 | } 921 | cdbus_m_opts_get_do(unredir_if_possible, cdbus_reply_bool); 922 | cdbus_m_opts_get_do(unredir_if_possible_delay, cdbus_reply_int32); 923 | cdbus_m_opts_get_do(redirected_force, cdbus_reply_enum); 924 | cdbus_m_opts_get_do(stoppaint_force, cdbus_reply_enum); 925 | cdbus_m_opts_get_do(logpath, cdbus_reply_string); 926 | cdbus_m_opts_get_do(synchronize, cdbus_reply_bool); 927 | 928 | cdbus_m_opts_get_do(refresh_rate, cdbus_reply_int32); 929 | cdbus_m_opts_get_do(sw_opti, cdbus_reply_bool); 930 | if (!strcmp("vsync", target)) { 931 | assert(ps->o.vsync < sizeof(VSYNC_STRS) / sizeof(VSYNC_STRS[0])); 932 | cdbus_reply_string(ps, msg, VSYNC_STRS[ps->o.vsync]); 933 | return true; 934 | } 935 | if (!strcmp("backend", target)) { 936 | assert(ps->o.backend < sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0])); 937 | cdbus_reply_string(ps, msg, BACKEND_STRS[ps->o.backend]); 938 | return true; 939 | } 940 | cdbus_m_opts_get_do(dbe, cdbus_reply_bool); 941 | cdbus_m_opts_get_do(vsync_aggressive, cdbus_reply_bool); 942 | 943 | cdbus_m_opts_get_do(shadow_red, cdbus_reply_double); 944 | cdbus_m_opts_get_do(shadow_green, cdbus_reply_double); 945 | cdbus_m_opts_get_do(shadow_blue, cdbus_reply_double); 946 | cdbus_m_opts_get_do(shadow_radius, cdbus_reply_int32); 947 | cdbus_m_opts_get_do(shadow_offset_x, cdbus_reply_int32); 948 | cdbus_m_opts_get_do(shadow_offset_y, cdbus_reply_int32); 949 | cdbus_m_opts_get_do(shadow_opacity, cdbus_reply_double); 950 | cdbus_m_opts_get_do(clear_shadow, cdbus_reply_bool); 951 | cdbus_m_opts_get_do(xinerama_shadow_crop, cdbus_reply_bool); 952 | 953 | cdbus_m_opts_get_do(fade_delta, cdbus_reply_int32); 954 | cdbus_m_opts_get_do(fade_in_step, cdbus_reply_int32); 955 | cdbus_m_opts_get_do(fade_out_step, cdbus_reply_int32); 956 | cdbus_m_opts_get_do(no_fading_openclose, cdbus_reply_bool); 957 | 958 | cdbus_m_opts_get_do(blur_background, cdbus_reply_bool); 959 | cdbus_m_opts_get_do(blur_background_frame, cdbus_reply_bool); 960 | cdbus_m_opts_get_do(blur_background_fixed, cdbus_reply_bool); 961 | 962 | cdbus_m_opts_get_do(inactive_dim, cdbus_reply_double); 963 | cdbus_m_opts_get_do(inactive_dim_fixed, cdbus_reply_bool); 964 | 965 | cdbus_m_opts_get_do(use_ewmh_active_win, cdbus_reply_bool); 966 | cdbus_m_opts_get_do(detect_transient, cdbus_reply_bool); 967 | cdbus_m_opts_get_do(detect_client_leader, cdbus_reply_bool); 968 | 969 | #ifdef CONFIG_VSYNC_OPENGL 970 | cdbus_m_opts_get_do(glx_no_stencil, cdbus_reply_bool); 971 | cdbus_m_opts_get_do(glx_copy_from_front, cdbus_reply_bool); 972 | cdbus_m_opts_get_do(glx_use_copysubbuffermesa, cdbus_reply_bool); 973 | cdbus_m_opts_get_do(glx_no_rebind_pixmap, cdbus_reply_bool); 974 | cdbus_m_opts_get_do(glx_swap_method, cdbus_reply_int32); 975 | #endif 976 | 977 | cdbus_m_opts_get_do(track_focus, cdbus_reply_bool); 978 | cdbus_m_opts_get_do(track_wdata, cdbus_reply_bool); 979 | cdbus_m_opts_get_do(track_leader, cdbus_reply_bool); 980 | #undef cdbus_m_opts_get_do 981 | 982 | printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); 983 | cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); 984 | 985 | return true; 986 | } 987 | 988 | /** 989 | * Process a opts_set D-Bus request. 990 | */ 991 | static bool 992 | cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { 993 | const char *target = NULL; 994 | 995 | if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) 996 | return false; 997 | 998 | #define cdbus_m_opts_set_do(tgt, type, real_type) \ 999 | if (!strcmp(MSTR(tgt), target)) { \ 1000 | real_type val; \ 1001 | if (!cdbus_msg_get_arg(msg, 1, type, &val)) \ 1002 | return false; \ 1003 | ps->o.tgt = val; \ 1004 | goto cdbus_process_opts_set_success; \ 1005 | } 1006 | 1007 | // fade_delta 1008 | if (!strcmp("fade_delta", target)) { 1009 | int32_t val = 0.0; 1010 | if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_INT32, &val)) 1011 | return false; 1012 | ps->o.fade_delta = max_i(val, 1); 1013 | goto cdbus_process_opts_set_success; 1014 | } 1015 | 1016 | // fade_in_step 1017 | if (!strcmp("fade_in_step", target)) { 1018 | double val = 0.0; 1019 | if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val)) 1020 | return false; 1021 | ps->o.fade_in_step = normalize_d(val) * OPAQUE; 1022 | goto cdbus_process_opts_set_success; 1023 | } 1024 | 1025 | // fade_out_step 1026 | if (!strcmp("fade_out_step", target)) { 1027 | double val = 0.0; 1028 | if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val)) 1029 | return false; 1030 | ps->o.fade_out_step = normalize_d(val) * OPAQUE; 1031 | goto cdbus_process_opts_set_success; 1032 | } 1033 | 1034 | // no_fading_openclose 1035 | if (!strcmp("no_fading_openclose", target)) { 1036 | dbus_bool_t val = FALSE; 1037 | if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) 1038 | return false; 1039 | opts_set_no_fading_openclose(ps, val); 1040 | goto cdbus_process_opts_set_success; 1041 | } 1042 | 1043 | // unredir_if_possible 1044 | if (!strcmp("unredir_if_possible", target)) { 1045 | dbus_bool_t val = FALSE; 1046 | if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) 1047 | return false; 1048 | if (ps->o.unredir_if_possible != val) { 1049 | ps->o.unredir_if_possible = val; 1050 | ps->ev_received = true; 1051 | } 1052 | goto cdbus_process_opts_set_success; 1053 | } 1054 | 1055 | // clear_shadow 1056 | if (!strcmp("clear_shadow", target)) { 1057 | dbus_bool_t val = FALSE; 1058 | if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) 1059 | return false; 1060 | if (ps->o.clear_shadow != val) { 1061 | ps->o.clear_shadow = val; 1062 | force_repaint(ps); 1063 | } 1064 | goto cdbus_process_opts_set_success; 1065 | } 1066 | 1067 | // track_focus 1068 | if (!strcmp("track_focus", target)) { 1069 | dbus_bool_t val = FALSE; 1070 | if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) 1071 | return false; 1072 | // You could enable this option, but never turn if off 1073 | if (val) { 1074 | opts_init_track_focus(ps); 1075 | } 1076 | goto cdbus_process_opts_set_success; 1077 | } 1078 | 1079 | // vsync 1080 | if (!strcmp("vsync", target)) { 1081 | const char * val = NULL; 1082 | if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_STRING, &val)) 1083 | return false; 1084 | vsync_deinit(ps); 1085 | if (!parse_vsync(ps, val)) { 1086 | printf_errf("(): " CDBUS_ERROR_BADARG_S, 1, "Value invalid."); 1087 | cdbus_reply_err(ps, msg, CDBUS_ERROR_BADARG, CDBUS_ERROR_BADARG_S, 1, "Value invalid."); 1088 | } 1089 | else if (!vsync_init(ps)) { 1090 | printf_errf("(): " CDBUS_ERROR_CUSTOM_S, "Failed to initialize specified VSync method."); 1091 | cdbus_reply_err(ps, msg, CDBUS_ERROR_CUSTOM, CDBUS_ERROR_CUSTOM_S, "Failed to initialize specified VSync method."); 1092 | } 1093 | else 1094 | goto cdbus_process_opts_set_success; 1095 | return true; 1096 | } 1097 | 1098 | // redirected_force 1099 | if (!strcmp("redirected_force", target)) { 1100 | cdbus_enum_t val = UNSET; 1101 | if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_ENUM, &val)) 1102 | return false; 1103 | ps->o.redirected_force = val; 1104 | force_repaint(ps); 1105 | goto cdbus_process_opts_set_success; 1106 | } 1107 | 1108 | // stoppaint_force 1109 | cdbus_m_opts_set_do(stoppaint_force, CDBUS_TYPE_ENUM, cdbus_enum_t); 1110 | 1111 | #undef cdbus_m_opts_set_do 1112 | 1113 | printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); 1114 | cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); 1115 | 1116 | return true; 1117 | 1118 | cdbus_process_opts_set_success: 1119 | if (!dbus_message_get_no_reply(msg)) 1120 | cdbus_reply_bool(ps, msg, true); 1121 | return true; 1122 | } 1123 | 1124 | /** 1125 | * Process an Introspect D-Bus request. 1126 | */ 1127 | static bool 1128 | cdbus_process_introspect(session_t *ps, DBusMessage *msg) { 1129 | const static char *str_introspect = 1130 | "\n" 1132 | "\n" 1133 | " \n" 1134 | " \n" 1135 | " \n" 1136 | " \n" 1137 | " \n" 1138 | " \n" 1139 | " \n" 1140 | " \n" 1141 | " \n" 1142 | " \n" 1143 | " \n" 1144 | " \n" 1145 | " \n" 1146 | " \n" 1147 | " \n" 1148 | " \n" 1149 | " \n" 1150 | " \n" 1151 | " \n" 1152 | " \n" 1153 | " \n" 1154 | " \n" 1155 | " \n" 1156 | " \n" 1157 | " \n" 1158 | " \n" 1159 | " \n" 1160 | " \n" 1161 | " \n" 1162 | " \n" 1163 | " \n" 1164 | " \n" 1165 | " \n" 1166 | "\n"; 1167 | 1168 | cdbus_reply_string(ps, msg, str_introspect); 1169 | 1170 | return true; 1171 | } 1172 | ///@} 1173 | 1174 | /** @name Core callbacks 1175 | */ 1176 | ///@{ 1177 | void 1178 | cdbus_ev_win_added(session_t *ps, win *w) { 1179 | if (ps->dbus_conn) 1180 | cdbus_signal_wid(ps, "win_added", w->id); 1181 | } 1182 | 1183 | void 1184 | cdbus_ev_win_destroyed(session_t *ps, win *w) { 1185 | if (ps->dbus_conn) 1186 | cdbus_signal_wid(ps, "win_destroyed", w->id); 1187 | } 1188 | 1189 | void 1190 | cdbus_ev_win_mapped(session_t *ps, win *w) { 1191 | if (ps->dbus_conn) 1192 | cdbus_signal_wid(ps, "win_mapped", w->id); 1193 | } 1194 | 1195 | void 1196 | cdbus_ev_win_unmapped(session_t *ps, win *w) { 1197 | if (ps->dbus_conn) 1198 | cdbus_signal_wid(ps, "win_unmapped", w->id); 1199 | } 1200 | 1201 | void 1202 | cdbus_ev_win_focusout(session_t *ps, win *w) { 1203 | if (ps->dbus_conn) 1204 | cdbus_signal_wid(ps, "win_focusout", w->id); 1205 | } 1206 | 1207 | void 1208 | cdbus_ev_win_focusin(session_t *ps, win *w) { 1209 | if (ps->dbus_conn) 1210 | cdbus_signal_wid(ps, "win_focusin", w->id); 1211 | } 1212 | //!@} 1213 | -------------------------------------------------------------------------------- /src/dbus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Compton - a compositor for X11 3 | * 4 | * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard 5 | * 6 | * Copyright (c) 2011-2013, Christopher Jeffrey 7 | * See LICENSE for more information. 8 | * 9 | */ 10 | 11 | #include "common.h" 12 | #include 13 | #include 14 | #include 15 | 16 | #define CDBUS_SERVICE_NAME "com.github.chjj.compton" 17 | #define CDBUS_INTERFACE_NAME CDBUS_SERVICE_NAME 18 | #define CDBUS_OBJECT_NAME "/com/github/chjj/compton" 19 | #define CDBUS_ERROR_PREFIX CDBUS_INTERFACE_NAME ".error" 20 | #define CDBUS_ERROR_UNKNOWN CDBUS_ERROR_PREFIX ".unknown" 21 | #define CDBUS_ERROR_UNKNOWN_S "Well, I don't know what happened. Do you?" 22 | #define CDBUS_ERROR_BADMSG CDBUS_ERROR_PREFIX ".bad_message" 23 | #define CDBUS_ERROR_BADMSG_S "Unrecognized command. Beware compton " \ 24 | "cannot make you a sandwich." 25 | #define CDBUS_ERROR_BADARG CDBUS_ERROR_PREFIX ".bad_argument" 26 | #define CDBUS_ERROR_BADARG_S "Failed to parse argument %d: %s" 27 | #define CDBUS_ERROR_BADWIN CDBUS_ERROR_PREFIX ".bad_window" 28 | #define CDBUS_ERROR_BADWIN_S "Requested window %#010lx not found." 29 | #define CDBUS_ERROR_BADTGT CDBUS_ERROR_PREFIX ".bad_target" 30 | #define CDBUS_ERROR_BADTGT_S "Target \"%s\" not found." 31 | #define CDBUS_ERROR_FORBIDDEN CDBUS_ERROR_PREFIX ".forbidden" 32 | #define CDBUS_ERROR_FORBIDDEN_S "Incorrect password, access denied." 33 | #define CDBUS_ERROR_CUSTOM CDBUS_ERROR_PREFIX ".custom" 34 | #define CDBUS_ERROR_CUSTOM_S "%s" 35 | 36 | // Window type 37 | typedef uint32_t cdbus_window_t; 38 | #define CDBUS_TYPE_WINDOW DBUS_TYPE_UINT32 39 | #define CDBUS_TYPE_WINDOW_STR DBUS_TYPE_UINT32_AS_STRING 40 | 41 | typedef uint16_t cdbus_enum_t; 42 | #define CDBUS_TYPE_ENUM DBUS_TYPE_UINT16 43 | #define CDBUS_TYPE_ENUM_STR DBUS_TYPE_UINT16_AS_STRING 44 | 45 | static dbus_bool_t 46 | cdbus_callback_add_timeout(DBusTimeout *timeout, void *data); 47 | 48 | static void 49 | cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data); 50 | 51 | static void 52 | cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data); 53 | 54 | static bool 55 | cdbus_callback_handle_timeout(session_t *ps, timeout_t *ptmout); 56 | 57 | /** 58 | * Determine the poll condition of a DBusWatch. 59 | */ 60 | static inline short 61 | cdbus_get_watch_cond(DBusWatch *watch) { 62 | const unsigned flags = dbus_watch_get_flags(watch); 63 | short condition = POLLERR | POLLHUP; 64 | if (flags & DBUS_WATCH_READABLE) 65 | condition |= POLLIN; 66 | if (flags & DBUS_WATCH_WRITABLE) 67 | condition |= POLLOUT; 68 | 69 | return condition; 70 | } 71 | 72 | static dbus_bool_t 73 | cdbus_callback_add_watch(DBusWatch *watch, void *data); 74 | 75 | static void 76 | cdbus_callback_remove_watch(DBusWatch *watch, void *data); 77 | 78 | static void 79 | cdbus_callback_watch_toggled(DBusWatch *watch, void *data); 80 | 81 | static bool 82 | cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data); 83 | 84 | static bool 85 | cdbus_apdarg_int32(session_t *ps, DBusMessage *msg, const void *data); 86 | 87 | static bool 88 | cdbus_apdarg_uint32(session_t *ps, DBusMessage *msg, const void *data); 89 | 90 | static bool 91 | cdbus_apdarg_double(session_t *ps, DBusMessage *msg, const void *data); 92 | 93 | static bool 94 | cdbus_apdarg_wid(session_t *ps, DBusMessage *msg, const void *data); 95 | 96 | static bool 97 | cdbus_apdarg_enum(session_t *ps, DBusMessage *msg, const void *data); 98 | 99 | static bool 100 | cdbus_apdarg_string(session_t *ps, DBusMessage *msg, const void *data); 101 | 102 | static bool 103 | cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data); 104 | 105 | /** @name DBus signal sending 106 | */ 107 | ///@{ 108 | 109 | static bool 110 | cdbus_signal(session_t *ps, const char *name, 111 | bool (*func)(session_t *ps, DBusMessage *msg, const void *data), 112 | const void *data); 113 | 114 | /** 115 | * Send a signal with no argument. 116 | */ 117 | static inline bool 118 | cdbus_signal_noarg(session_t *ps, const char *name) { 119 | return cdbus_signal(ps, name, NULL, NULL); 120 | } 121 | 122 | /** 123 | * Send a signal with a Window ID as argument. 124 | */ 125 | static inline bool 126 | cdbus_signal_wid(session_t *ps, const char *name, Window wid) { 127 | return cdbus_signal(ps, name, cdbus_apdarg_wid, &wid); 128 | } 129 | 130 | ///@} 131 | 132 | /** @name DBus reply sending 133 | */ 134 | ///@{ 135 | 136 | static bool 137 | cdbus_reply(session_t *ps, DBusMessage *srcmsg, 138 | bool (*func)(session_t *ps, DBusMessage *msg, const void *data), 139 | const void *data); 140 | 141 | static bool 142 | cdbus_reply_errm(session_t *ps, DBusMessage *msg); 143 | 144 | #define cdbus_reply_err(ps, srcmsg, err_name, err_format, ...) \ 145 | cdbus_reply_errm((ps), dbus_message_new_error_printf((srcmsg), (err_name), (err_format), ## __VA_ARGS__)) 146 | 147 | /** 148 | * Send a reply with no argument. 149 | */ 150 | static inline bool 151 | cdbus_reply_noarg(session_t *ps, DBusMessage *srcmsg) { 152 | return cdbus_reply(ps, srcmsg, NULL, NULL); 153 | } 154 | 155 | /** 156 | * Send a reply with a bool argument. 157 | */ 158 | static inline bool 159 | cdbus_reply_bool(session_t *ps, DBusMessage *srcmsg, bool bval) { 160 | return cdbus_reply(ps, srcmsg, cdbus_apdarg_bool, &bval); 161 | } 162 | 163 | /** 164 | * Send a reply with an int32 argument. 165 | */ 166 | static inline bool 167 | cdbus_reply_int32(session_t *ps, DBusMessage *srcmsg, int32_t val) { 168 | return cdbus_reply(ps, srcmsg, cdbus_apdarg_int32, &val); 169 | } 170 | 171 | /** 172 | * Send a reply with an uint32 argument. 173 | */ 174 | static inline bool 175 | cdbus_reply_uint32(session_t *ps, DBusMessage *srcmsg, uint32_t val) { 176 | return cdbus_reply(ps, srcmsg, cdbus_apdarg_uint32, &val); 177 | } 178 | 179 | /** 180 | * Send a reply with a double argument. 181 | */ 182 | static inline bool 183 | cdbus_reply_double(session_t *ps, DBusMessage *srcmsg, double val) { 184 | return cdbus_reply(ps, srcmsg, cdbus_apdarg_double, &val); 185 | } 186 | 187 | /** 188 | * Send a reply with a wid argument. 189 | */ 190 | static inline bool 191 | cdbus_reply_wid(session_t *ps, DBusMessage *srcmsg, Window wid) { 192 | return cdbus_reply(ps, srcmsg, cdbus_apdarg_wid, &wid); 193 | } 194 | 195 | /** 196 | * Send a reply with a string argument. 197 | */ 198 | static inline bool 199 | cdbus_reply_string(session_t *ps, DBusMessage *srcmsg, const char *str) { 200 | return cdbus_reply(ps, srcmsg, cdbus_apdarg_string, str); 201 | } 202 | 203 | /** 204 | * Send a reply with a enum argument. 205 | */ 206 | static inline bool 207 | cdbus_reply_enum(session_t *ps, DBusMessage *srcmsg, cdbus_enum_t eval) { 208 | return cdbus_reply(ps, srcmsg, cdbus_apdarg_enum, &eval); 209 | } 210 | 211 | ///@} 212 | 213 | static bool 214 | cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest); 215 | 216 | /** 217 | * Return a string representation of a D-Bus message type. 218 | */ 219 | static inline const char * 220 | cdbus_repr_msgtype(DBusMessage *msg) { 221 | return dbus_message_type_to_string(dbus_message_get_type(msg)); 222 | } 223 | 224 | /** @name Message processing 225 | */ 226 | ///@{ 227 | 228 | static void 229 | cdbus_process(session_t *ps, DBusMessage *msg); 230 | 231 | static bool 232 | cdbus_process_list_win(session_t *ps, DBusMessage *msg); 233 | 234 | static bool 235 | cdbus_process_win_get(session_t *ps, DBusMessage *msg); 236 | 237 | static bool 238 | cdbus_process_win_set(session_t *ps, DBusMessage *msg); 239 | 240 | static bool 241 | cdbus_process_find_win(session_t *ps, DBusMessage *msg); 242 | 243 | static bool 244 | cdbus_process_opts_get(session_t *ps, DBusMessage *msg); 245 | 246 | static bool 247 | cdbus_process_opts_set(session_t *ps, DBusMessage *msg); 248 | 249 | static bool 250 | cdbus_process_introspect(session_t *ps, DBusMessage *msg); 251 | 252 | ///@} 253 | -------------------------------------------------------------------------------- /src/opengl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Compton - a compositor for X11 3 | * 4 | * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard 5 | * 6 | * Copyright (c) 2011-2013, Christopher Jeffrey 7 | * See LICENSE for more information. 8 | * 9 | */ 10 | 11 | #include "common.h" 12 | 13 | #include 14 | #include 15 | 16 | #ifdef DEBUG_GLX_ERR 17 | 18 | /** 19 | * Get a textual representation of an OpenGL error. 20 | */ 21 | static inline const char * 22 | glx_dump_err_str(GLenum err) { 23 | switch (err) { 24 | CASESTRRET(GL_NO_ERROR); 25 | CASESTRRET(GL_INVALID_ENUM); 26 | CASESTRRET(GL_INVALID_VALUE); 27 | CASESTRRET(GL_INVALID_OPERATION); 28 | CASESTRRET(GL_INVALID_FRAMEBUFFER_OPERATION); 29 | CASESTRRET(GL_OUT_OF_MEMORY); 30 | CASESTRRET(GL_STACK_UNDERFLOW); 31 | CASESTRRET(GL_STACK_OVERFLOW); 32 | } 33 | 34 | return NULL; 35 | } 36 | 37 | /** 38 | * Check for GLX error. 39 | * 40 | * http://blog.nobel-joergensen.com/2013/01/29/debugging-opengl-using-glgeterror/ 41 | */ 42 | static inline void 43 | glx_check_err_(session_t *ps, const char *func, int line) { 44 | if (!ps->psglx->context) return; 45 | 46 | GLenum err = GL_NO_ERROR; 47 | 48 | while (GL_NO_ERROR != (err = glGetError())) { 49 | print_timestamp(ps); 50 | printf("%s():%d: GLX error ", func, line); 51 | const char *errtext = glx_dump_err_str(err); 52 | if (errtext) { 53 | printf_dbg("%s\n", errtext); 54 | } 55 | else { 56 | printf_dbg("%d\n", err); 57 | } 58 | } 59 | } 60 | 61 | #define glx_check_err(ps) glx_check_err_(ps, __func__, __LINE__) 62 | #else 63 | #define glx_check_err(ps) ((void) 0) 64 | #endif 65 | 66 | /** 67 | * Check if a word is in string. 68 | */ 69 | static inline bool 70 | wd_is_in_str(const char *haystick, const char *needle) { 71 | if (!haystick) 72 | return false; 73 | 74 | assert(*needle); 75 | 76 | const char *pos = haystick - 1; 77 | while ((pos = strstr(pos + 1, needle))) { 78 | // Continue if it isn't a word boundary 79 | if (((pos - haystick) && !isspace(*(pos - 1))) 80 | || (strlen(pos) > strlen(needle) && !isspace(pos[strlen(needle)]))) 81 | continue; 82 | return true; 83 | } 84 | 85 | return false; 86 | } 87 | 88 | /** 89 | * Check if a GLX extension exists. 90 | */ 91 | static inline bool 92 | glx_hasglxext(session_t *ps, const char *ext) { 93 | const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr); 94 | if (!glx_exts) { 95 | printf_errf("(): Failed get GLX extension list."); 96 | return false; 97 | } 98 | 99 | bool found = wd_is_in_str(glx_exts, ext); 100 | if (!found) 101 | printf_errf("(): Missing GLX extension %s.", ext); 102 | 103 | return found; 104 | } 105 | 106 | /** 107 | * Check if a GLX extension exists. 108 | */ 109 | static inline bool 110 | glx_hasglext(session_t *ps, const char *ext) { 111 | const char *gl_exts = (const char *) glGetString(GL_EXTENSIONS); 112 | if (!gl_exts) { 113 | printf_errf("(): Failed get GL extension list."); 114 | return false; 115 | } 116 | 117 | bool found = wd_is_in_str(gl_exts, ext); 118 | if (!found) 119 | printf_errf("(): Missing GL extension %s.", ext); 120 | 121 | return found; 122 | } 123 | 124 | static inline XVisualInfo * 125 | get_visualinfo_from_visual(session_t *ps, Visual *visual) { 126 | XVisualInfo vreq = { .visualid = XVisualIDFromVisual(visual) }; 127 | int nitems = 0; 128 | 129 | return XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems); 130 | } 131 | 132 | static bool 133 | glx_update_fbconfig(session_t *ps); 134 | 135 | static int 136 | glx_cmp_fbconfig(session_t *ps, 137 | const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b); 138 | 139 | static void 140 | glx_render_color(session_t *ps, int dx, int dy, int width, int height, int z, 141 | XserverRegion reg_tgt, const reg_data_t *pcache_reg); 142 | 143 | static void 144 | glx_render_dots(session_t *ps, int dx, int dy, int width, int height, int z, 145 | XserverRegion reg_tgt, const reg_data_t *pcache_reg); 146 | -------------------------------------------------------------------------------- /src/xrescheck.c: -------------------------------------------------------------------------------- 1 | #include "xrescheck.h" 2 | 3 | static xrc_xid_record_t *gs_xid_records = NULL; 4 | 5 | #define HASH_ADD_XID(head, xidfield, add) \ 6 | HASH_ADD(hh, head, xidfield, sizeof(xid), add) 7 | 8 | #define HASH_FIND_XID(head, findxid, out) \ 9 | HASH_FIND(hh, head, findxid, sizeof(xid), out) 10 | 11 | #define M_CPY_POS_DATA(prec) \ 12 | prec->file = file; \ 13 | prec->func = func; \ 14 | prec->line = line; \ 15 | 16 | /** 17 | * @brief Add a record of given XID to the allocation table. 18 | */ 19 | void 20 | xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS) { 21 | xrc_xid_record_t *prec = cmalloc(1, xrc_xid_record_t); 22 | prec->xid = xid; 23 | prec->type = type; 24 | M_CPY_POS_DATA(prec); 25 | 26 | HASH_ADD_XID(gs_xid_records, xid, prec); 27 | } 28 | 29 | /** 30 | * @brief Delete a record of given XID in the allocation table. 31 | */ 32 | void 33 | xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS) { 34 | xrc_xid_record_t *prec = NULL; 35 | HASH_FIND_XID(gs_xid_records, &xid, prec); 36 | if (!prec) { 37 | printf_err("XRC: %s:%d %s(): Can't find XID %#010lx we want to delete.", 38 | file, line, func, xid); 39 | return; 40 | } 41 | HASH_DEL(gs_xid_records, prec); 42 | free(prec); 43 | } 44 | 45 | /** 46 | * @brief Report about issues found in the XID allocation table. 47 | */ 48 | void 49 | xrc_report_xid(void) { 50 | for (xrc_xid_record_t *prec = gs_xid_records; prec; prec = prec->hh.next) 51 | printf_dbg("XRC: %s:%d %s(): %#010lx (%s) not freed.\n", 52 | prec->file, prec->line, prec->func, prec->xid, prec->type); 53 | } 54 | 55 | /** 56 | * @brief Clear the XID allocation table. 57 | */ 58 | void 59 | xrc_clear_xid(void) { 60 | xrc_xid_record_t *prec = NULL, *ptmp = NULL; 61 | HASH_ITER(hh, gs_xid_records, prec, ptmp) { 62 | HASH_DEL(gs_xid_records, prec); 63 | free(prec); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/xrescheck.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPTON_XRESCHECK_H 2 | #define COMPTON_XRESCHECK_H 3 | 4 | #include "common.h" 5 | #include 6 | 7 | typedef struct { 8 | XID xid; 9 | const char *type; 10 | const char *file; 11 | const char *func; 12 | int line; 13 | UT_hash_handle hh; 14 | } xrc_xid_record_t; 15 | 16 | #define M_POS_DATA_PARAMS const char *file, int line, const char *func 17 | #define M_POS_DATA_PASSTHROUGH file, line, func 18 | #define M_POS_DATA __FILE__, __LINE__, __func__ 19 | 20 | void 21 | xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS); 22 | 23 | #define xrc_add_xid(xid, type) xrc_add_xid_(xid, type, M_POS_DATA) 24 | 25 | void 26 | xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS); 27 | 28 | #define xrc_delete_xid(xid) xrc_delete_xid_(xid, M_POS_DATA) 29 | 30 | void 31 | xrc_report_xid(void); 32 | 33 | void 34 | xrc_clear_xid(void); 35 | 36 | // Pixmap 37 | 38 | static inline Pixmap 39 | XCreatePixmap_(Display *dpy, Drawable drawable, 40 | unsigned int width, unsigned int height, unsigned int depth, 41 | M_POS_DATA_PARAMS) { 42 | Pixmap ret = XCreatePixmap(dpy, drawable, width, height, depth); 43 | if (ret) 44 | xrc_add_xid_(ret, "Pixmap", M_POS_DATA_PASSTHROUGH); 45 | return ret; 46 | } 47 | 48 | #define XCreatePixmap(dpy, drawable, width, height, depth) \ 49 | XCreatePixmap_(dpy, drawable, width, height, depth, M_POS_DATA) 50 | 51 | static inline Pixmap 52 | XCompositeNameWindowPixmap_(Display *dpy, Window window, M_POS_DATA_PARAMS) { 53 | Pixmap ret = XCompositeNameWindowPixmap(dpy, window); 54 | if (ret) 55 | xrc_add_xid_(ret, "PixmapC", M_POS_DATA_PASSTHROUGH); 56 | return ret; 57 | } 58 | 59 | #define XCompositeNameWindowPixmap(dpy, window) \ 60 | XCompositeNameWindowPixmap_(dpy, window, M_POS_DATA) 61 | 62 | static inline void 63 | XFreePixmap_(Display *dpy, Pixmap pixmap, M_POS_DATA_PARAMS) { 64 | XFreePixmap(dpy, pixmap); 65 | xrc_delete_xid_(pixmap, M_POS_DATA_PASSTHROUGH); 66 | } 67 | 68 | #define XFreePixmap(dpy, pixmap) XFreePixmap_(dpy, pixmap, M_POS_DATA); 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /tests/cmake-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Test script for CMake build 4 | 5 | BASE_DIR=$(dirname "$0")/.. 6 | . "${BASE_DIR}/functions.sh" 7 | 8 | BUILD_DIR="build" 9 | 10 | cmake_prepare() { 11 | [ ! -e "CMakeLists.txt" ] && ln -s {_,}CMakeLists.txt 12 | } 13 | 14 | cmake_build() { 15 | einfo Building compton with cmake $@ 16 | 17 | [ -e "${BUILD_DIR}" ] && rm -r "${BUILD_DIR}" 18 | mkdir "${BUILD_DIR}" && cd "${BUILD_DIR}" || die 19 | cmake ${@} .. || die 20 | make VERBOSE=1 -B || die 21 | cd - 22 | 23 | einfo Build completed successfully 24 | } 25 | 26 | show_build_help_msg() { 27 | "${BUILD_DIR}/compton" -h | less 28 | } 29 | 30 | main() { 31 | cmake_prepare 32 | cmake_build "${@}" 33 | # show_build_help_msg 34 | } 35 | 36 | main "${@}" 37 | -------------------------------------------------------------------------------- /tests/make-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Test script for GNU make build 4 | 5 | BASE_DIR=$(dirname "$0")/.. 6 | . "${BASE_DIR}/functions.sh" 7 | 8 | OPTIONS=( NO_XINERAMA NO_LIBCONFIG NO_REGEX_PCRE NO_REGEX_PCRE_JIT 9 | NO_VSYNC_DRM NO_VSYNC_OPENGL NO_VSYNC_OPENGL_GLSL NO_VSYNC_OPENGL_FBO 10 | NO_VSYNC_OPENGL_VBO NO_DBUS NO_XSYNC NO_C2 ) 11 | 12 | for o in "${OPTIONS[@]}"; do 13 | einfo Building with $o 14 | make "${o}=1" -B "${@}" || die 15 | einfo Build completed. 16 | done 17 | --------------------------------------------------------------------------------