pax_global_header00006660000000000000000000000064126001210240014476gustar00rootroot0000000000000052 comment=2343e4bbd298b35ea5c190c52abd2b0cb9f79a18 compton-0.1~beta2+20150922/000077500000000000000000000000001260012102400150315ustar00rootroot00000000000000compton-0.1~beta2+20150922/.gitignore000066400000000000000000000010751260012102400170240ustar00rootroot00000000000000# Build files .deps aclocal.m4 autom4te.cache config.h config.h.in config.log config.status configure depcomp install-sh missing stamp-h1 compton *.o build/ # CMake files compton-*.deb compton-*.rpm compton-*.tar.bz2 release/ _CPack_Packages/ CMakeCache.txt CMakeFiles/ cmake_install.cmake CPackSourceConfig.cmake install_manifest.txt # Vim files .sw[a-z] .*.sw[a-z] *~ # Misc files /core /*.core oprofile_data/ compton.plist callgrind.out.* man/*.html man/*.1 doxygen/ .clang_complete .ycm_extra_conf.py .ycm_extra_conf.pyc /src/backtrace-symbols.[ch] /compton*.trace compton-0.1~beta2+20150922/CPackConfig.cmake000066400000000000000000000036121260012102400201440ustar00rootroot00000000000000# == Environment == if (NOT CPACK_SYSTEM_NAME) set(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_PROCESSOR}") if (CPACK_SYSTEM_NAME STREQUAL "x86_64") set(CPACK_SYSTEM_NAME "amd64") endif () endif () # == Basic information == set(CPACK_PACKAGE_NAME "compton") set(CPACK_PACKAGE_VENDOR "chjj") set(CPACK_PACKAGE_VERSION_MAJOR "0") set(CPACK_PACKAGE_VERSION_MINOR "0") set(CPACK_PACKAGE_VERSION_PATCH "0") set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") set(CPACK_PACKAGE_DESCRIPTION "A lightweight X compositing window manager, fork of xcompmgr-dana.") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A lightweight X compositing window manager") set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_SYSTEM_NAME}") set(CPACK_PACKAGE_CONTACT "nobody ") set(CPACK_INSTALL_COMMANDS "env PREFIX=build make install") # == Package config == set(CPACK_INSTALLED_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/build" "usr") set(CPACK_GENERATOR "TBZ2" "DEB" "RPM") set(CPACK_RESOURCE_FILE_LICENSE "LICENSE") set(CPACK_RESOURCE_FILE_README "README.md") set(CPACK_STRIP_FILES 1) # == DEB package config == set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "${CPACK_SYSTEM_NAME}") set(CPACK_DEBIAN_PACKAGE_SECTION "x11") 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") # == RPM package config == 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") # == Source package config == set(CPACK_SOURCE_GENERATOR "TBZ2 DEB RPM") compton-0.1~beta2+20150922/Doxyfile000066400000000000000000002266641260012102400165570ustar00rootroot00000000000000# Doxyfile 1.8.2 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = "compton" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doxygen # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented classes, # or namespaces to their corresponding documentation. Such a link can be # prevented in individual cases by by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter and setter methods for a property. Setting this option to YES (the default) will make doxygen replace the get and set methods by a property in the documentation. This will only work if the methods are indeed getting or setting a simple type. If this is not the case, or you want to show the methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = *.c *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefor more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = __attribute__(x)= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # managable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES compton-0.1~beta2+20150922/LICENSE000066400000000000000000000050561260012102400160440ustar00rootroot00000000000000compton - a compositor for X11 Contributors Based on xcompmgr, originally written by Keith Packard, with modifications from several contributors (according to the xcompmgr man page): Matthew Allum, Eric Anholt, Dan Doel, Thomas Luebking, Matthew Hawn, Ely Levy, Phil Blundell, and Carl Worth. Menu transparency was implemented by Dana Jansens. Numerous contributions to compton from Richard Grenville. xcompmgr Copyright © 2003 Keith Packard Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Keith Packard not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Keith Packard makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. compton Copyright (c) 2011, Christopher Jeffrey (github.com/chjj) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. compton-0.1~beta2+20150922/Makefile000066400000000000000000000120221260012102400164660ustar00rootroot00000000000000# Use tab to indent recipe lines, spaces to indent other lines, otherwise # GNU make may get unhappy. CC ?= gcc PREFIX ?= /usr BINDIR ?= $(PREFIX)/bin MANDIR ?= $(PREFIX)/share/man/man1 APPDIR ?= $(PREFIX)/share/applications ICODIR ?= $(PREFIX)/share/icons/hicolor/ PACKAGES = x11 xcomposite xfixes xdamage xrender xext xrandr LIBS = -lm -lrt INCS = OBJS = compton.o # === Configuration flags === CFG = -std=c99 # ==== Xinerama ==== # Enables support for --xinerama-shadow-crop ifeq "$(NO_XINERAMA)" "" CFG += -DCONFIG_XINERAMA PACKAGES += xinerama endif # ==== libconfig ==== # Enables configuration file parsing support ifeq "$(NO_LIBCONFIG)" "" CFG += -DCONFIG_LIBCONFIG PACKAGES += libconfig # libconfig-1.3* does not define LIBCONFIG_VER* macros, so we use # pkg-config to determine its version here CFG += $(shell pkg-config --atleast-version=1.4 libconfig || echo '-DCONFIG_LIBCONFIG_LEGACY') endif # ==== PCRE regular expression ==== # Enables support for PCRE regular expression pattern in window conditions ifeq "$(NO_REGEX_PCRE)" "" CFG += -DCONFIG_REGEX_PCRE LIBS += $(shell pcre-config --libs) INCS += $(shell pcre-config --cflags) # Enables JIT support in libpcre ifeq "$(NO_REGEX_PCRE_JIT)" "" CFG += -DCONFIG_REGEX_PCRE_JIT endif endif # ==== DRM VSync ==== # Enables support for "drm" VSync method ifeq "$(NO_VSYNC_DRM)" "" INCS += $(shell pkg-config --cflags libdrm) CFG += -DCONFIG_VSYNC_DRM endif # ==== OpenGL ==== # Enables support for GLX backend, OpenGL VSync methods, etc. ifeq "$(NO_VSYNC_OPENGL)" "" CFG += -DCONFIG_VSYNC_OPENGL # -lGL must precede some other libraries, or it segfaults on FreeBSD (#74) LIBS := -lGL $(LIBS) OBJS += opengl.o # Enables support for GLSL (GLX background blur, etc.) ifeq "$(NO_VSYNC_OPENGL_GLSL)" "" CFG += -DCONFIG_VSYNC_OPENGL_GLSL endif # Enables support for GL FBO (GLX multi-pass blur, etc.) ifeq "$(NO_VSYNC_OPENGL_FBO)" "" CFG += -DCONFIG_VSYNC_OPENGL_FBO endif # Enables support for GL VBO (does nothing right now) ifeq "$(NO_VSYNC_OPENGL_VBO)" "" CFG += -DCONFIG_VSYNC_OPENGL_VBO endif endif # ==== D-Bus ==== # Enables support for --dbus (D-Bus remote control) ifeq "$(NO_DBUS)" "" CFG += -DCONFIG_DBUS PACKAGES += dbus-1 OBJS += dbus.o endif # ==== X Sync ==== # Enables support for --xrender-sync-fence ifeq "$(NO_XSYNC)" "" CFG += -DCONFIG_XSYNC endif # ==== C2 ==== # Enable window condition support ifeq "$(NO_C2)" "" CFG += -DCONFIG_C2 OBJS += c2.o endif # ==== X resource checker ==== # Enable X resource leakage checking (Pixmap only, presently) ifneq "$(ENABLE_XRESCHECK)" "" CFG += -DDEBUG_XRC OBJS += xrescheck.o endif # === Version string === COMPTON_VERSION ?= git-$(shell git describe --always --dirty)-$(shell git log -1 --date=short --pretty=format:%cd) CFG += -DCOMPTON_VERSION="\"$(COMPTON_VERSION)\"" LDFLAGS ?= -Wl,-O1 -Wl,--as-needed ifeq "$(CFG_DEV)" "" CFLAGS ?= -DNDEBUG -O2 -D_FORTIFY_SOURCE=2 else CC = clang export LD_ALTEXEC = /usr/bin/ld.gold OBJS += backtrace-symbols.o LIBS += -lbfd CFLAGS += -ggdb -Wshadow # CFLAGS += -Weverything -Wno-disabled-macro-expansion -Wno-padded -Wno-gnu endif LIBS += $(shell pkg-config --libs $(PACKAGES)) INCS += $(shell pkg-config --cflags $(PACKAGES)) CFLAGS += -Wall BINS = compton bin/compton-trans MANPAGES = man/compton.1 man/compton-trans.1 MANPAGES_HTML = $(addsuffix .html,$(MANPAGES)) # === Recipes === .DEFAULT_GOAL := compton src/.clang_complete: Makefile @(for i in $(filter-out -O% -DNDEBUG, $(CFG) $(CPPFLAGS) $(CFLAGS) $(INCS)); do echo "$$i"; done) > $@ %.o: src/%.c src/%.h src/common.h $(CC) $(CFG) $(CPPFLAGS) $(CFLAGS) $(INCS) -c src/$*.c compton: $(OBJS) $(CC) $(CFG) $(CPPFLAGS) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJS) $(LIBS) man/%.1: man/%.1.asciidoc a2x --format manpage $< man/%.1.html: man/%.1.asciidoc asciidoc $< docs: $(MANPAGES) $(MANPAGES_HTML) install: $(BINS) docs @install -d "$(DESTDIR)$(BINDIR)" "$(DESTDIR)$(MANDIR)" "$(DESTDIR)$(APPDIR)" @install -m755 $(BINS) "$(DESTDIR)$(BINDIR)"/ ifneq "$(MANPAGES)" "" @install -m644 $(MANPAGES) "$(DESTDIR)$(MANDIR)"/ endif @install -d \ "$(DESTDIR)$(ICODIR)/scalable/apps" \ "$(DESTDIR)$(ICODIR)/48x48/apps" @install -m644 media/compton.svg "$(DESTDIR)$(ICODIR)/scalable/apps"/ @install -m644 media/icons/48x48/compton.png "$(DESTDIR)$(ICODIR)/48x48/apps"/ @install -m644 compton.desktop "$(DESTDIR)$(APPDIR)"/ ifneq "$(DOCDIR)" "" @install -d "$(DESTDIR)$(DOCDIR)" @install -m644 README.md compton.sample.conf "$(DESTDIR)$(DOCDIR)"/ @install -m755 dbus-examples/cdbus-driver.sh "$(DESTDIR)$(DOCDIR)"/ endif uninstall: @rm -f "$(DESTDIR)$(BINDIR)/compton" "$(DESTDIR)$(BINDIR)/compton-trans" @rm -f $(addprefix "$(DESTDIR)$(MANDIR)"/, compton.1 compton-trans.1) @rm -f "$(DESTDIR)$(APPDIR)/compton.desktop" ifneq "$(DOCDIR)" "" @rm -f $(addprefix "$(DESTDIR)$(DOCDIR)"/, README.md compton.sample.conf cdbus-driver.sh) endif clean: @rm -f $(OBJS) compton $(MANPAGES) $(MANPAGES_HTML) .clang_complete version: @echo "$(COMPTON_VERSION)" .PHONY: uninstall clean docs version compton-0.1~beta2+20150922/README.md000066400000000000000000000113771260012102400163210ustar00rootroot00000000000000# Compton [![Join the chat at https://gitter.im/chjj/compton](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/chjj/compton?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) __Compton__ is a compositor for X, and a fork of __xcompmgr-dana__. I was frustrated by the low amount of standalone lightweight compositors. Compton was forked from Dana Jansens' fork of xcompmgr and refactored. I fixed whatever bug I found, and added features I wanted. Things seem stable, but don't quote me on it. I will most likely be actively working on this until I get the features I want. This is also a learning experience for me. That is, I'm partially doing this out of a desire to learn Xlib. ## Changes from xcompmgr: * OpenGL backend (`--backend glx`), in addition to the old X Render backend. * Inactive window transparency (`-i`) / dimming (`--inactive-dim`). * Titlebar/frame transparency (`-e`). * Menu transparency (`-m`, thanks to Dana). * shadows are now enabled for argb windows, e.g. terminals with transparency * removed serverside shadows (and simple compositing) to clean the code, the only option that remains is clientside shadows * configuration files (see the man page for more details) * colored shadows (`--shadow-[red/green/blue]`) * a new fade system * VSync support (not always working) * Blur of background of transparent windows, window color inversion (bad in performance) * Some more options... ## Fixes from the original xcompmgr: * fixed a segfault when opening certain window types * fixed a memory leak caused by not freeing up shadows (from the freedesktop repo) * fixed the conflict with chromium and similar windows * [many more](https://github.com/chjj/compton/issues) ## Building ### Dependencies: __B__ for build-time __R__ for runtime * libx11 (B,R) * libxcomposite (B,R) * libxdamage (B,R) * libxfixes (B,R) * libXext (B,R) * libxrender (B,R) * libXrandr (B,R) * libXinerama (B,R) (Can be disabled with `NO_XINERAMA` at compile time) * pkg-config (B) * make (B) * xproto / x11proto (B) * sh (R) * xprop,xwininfo / x11-utils (R) * libpcre (B,R) (Can be disabled with `NO_REGEX_PCRE` at compile time) * libconfig (B,R) (Can be disabled with `NO_LIBCONFIG` at compile time) * libdrm (B) (Can be disabled with `NO_VSYNC_DRM` at compile time) * libGL (B,R) (Can be disabled with `NO_VSYNC_OPENGL` at compile time) * libdbus (B,R) (Can be disabled with `NO_DBUS` at compile time) * asciidoc (B) (and docbook-xml-dtd-4.5, libxml-utils, libxslt, xsltproc, xmlto, etc. if your distro doesn't pull them in) ### How to build To build, make sure you have the dependencies above: ```bash # Make the main program $ make # Make the man pages $ make docs # Install $ make install ``` (Compton does include a `_CMakeLists.txt` in the tree, but we haven't decided whether we should switch to CMake yet. The `Makefile` is fully usable right now.) ## Known issues * Our [FAQ](https://github.com/chjj/compton/wiki/faq) covers some known issues. * VSync does not work too well. You may check the [VSync Guide](https://github.com/chjj/compton/wiki/vsync-guide) for how to get (possibly) better effects. * If `--unredir-if-possible` is enabled, when compton redirects/unredirects windows, the screen may flicker. Using `--paint-on-overlay` minimizes the problem from my observation, yet I do not know if there's a cure. * compton may not track focus correctly in all situations. The focus tracking code is experimental. `--use-ewmh-active-win` might be helpful. * The performance of blur under X Render backend might be pretty bad. OpenGL backend could be faster. * With `--blur-background` you may sometimes see weird lines around damaged area. `--resize-damage YOUR_BLUR_RADIUS` might be helpful in the case. ## Usage Please refer to the Asciidoc man pages (`man/compton.1.asciidoc` & `man/compton-trans.1.asciidoc`) for more details and examples. Note a sample configuration file `compton.sample.conf` is included in the repository. ## Support * Bug reports and feature requests should go to the "Issues" section above. * Our (semi?) official IRC channel is #compton on FreeNode. * Some information is available on the wiki, including [FAQ](https://github.com/chjj/compton/wiki/faq), [VSync Guide](https://github.com/chjj/compton/wiki/vsync-guide), and [Performance Guide](https://github.com/chjj/compton/wiki/perf-guide). ## License Although compton has kind of taken on a life of its own, it was originally an xcompmgr fork. xcompmgr has gotten around. As far as I can tell, the lineage for this particular tree is something like: * Keith Packard (original author) * Matthew Hawn * ... * Dana Jansens * chjj and richardgv Not counting the tens of people who forked it in between. Compton is distributed under MIT license, as far as I (richardgv) know. See LICENSE for more info. compton-0.1~beta2+20150922/_CMakeLists.txt000066400000000000000000000145571260012102400177440ustar00rootroot00000000000000project(compton) cmake_minimum_required(VERSION 2.8) set(CPACK_PACKAGE_VERSION_MAJOR "0") set(CPACK_PACKAGE_VERSION_MINOR "0") set(CPACK_PACKAGE_VERSION_PATCH "0") add_subdirectory(man) set(compton_SRCS src/compton.c) set(CMAKE_C_FLAGS_DEBUG "-ggdb") set(CMAKE_C_FLAGS_RELEASE "-O2 -march=native") set(CMAKE_C_FLAGS_RELWITHDBGINFO "-O2 -march=native -ggdb") add_definitions("-Wall" "-std=c99") # == Version == execute_process(COMMAND sh -c "echo -n \\\"git-$(git describe --always --dirty)-$(git log -1 --date=short --pretty=format:%cd)\\\"" WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} OUTPUT_VARIABLE COMPTON_VERSION) add_definitions("-DCOMPTON_VERSION=${COMPTON_VERSION}") # == Options == include(CMakeDependentOption) option(CONFIG_REGEX_PCRE "Enable PCRE regular expression support for blacklist entries (requires libpcre)" ON) if (CONFIG_REGEX_PCRE) add_definitions("-DCONFIG_REGEX_PCRE") endif () option(CONFIG_REGEX_PCRE_JIT "Use JIT support of libpcre)" ON) if (CONFIG_REGEX_PCRE_JIT) add_definitions("-DCONFIG_REGEX_PCRE_JIT") endif () option(CONFIG_LIBCONFIG "Enable configuration file parsing using libconfig" ON) if (CONFIG_LIBCONFIG) add_definitions("-DCONFIG_LIBCONFIG") endif () option(CONFIG_VSYNC_DRM "Enable DRM VSync support" ON) if (CONFIG_VSYNC_DRM) add_definitions("-DCONFIG_VSYNC_DRM") endif () option(CONFIG_VSYNC_OPENGL "Enable OpenGL support" ON) if (CONFIG_VSYNC_OPENGL) add_definitions("-DCONFIG_VSYNC_OPENGL") list(APPEND compton_SRCS src/opengl.c) endif () CMAKE_DEPENDENT_OPTION(CONFIG_VSYNC_OPENGL_GLSL "Enable GLSL support (GLX background blur, etc.)" ON "CONFIG_VSYNC_OPENGL" OFF) if (CONFIG_VSYNC_OPENGL_GLSL) add_definitions("-DCONFIG_VSYNC_OPENGL_GLSL") endif () CMAKE_DEPENDENT_OPTION(CONFIG_VSYNC_OPENGL_FBO "Enable OpenGL FBO support (GLX multi-pass blur, etc.)" ON "CONFIG_VSYNC_OPENGL" OFF) if (CONFIG_VSYNC_OPENGL_FBO) add_definitions("-DCONFIG_VSYNC_OPENGL_FBO") endif () CMAKE_DEPENDENT_OPTION(CONFIG_VSYNC_OPENGL_VBO "Enable OpenGL VBO support (does nothing right now)" ON "CONFIG_VSYNC_OPENGL" OFF) if (CONFIG_VSYNC_OPENGL_VBO) add_definitions("-DCONFIG_VSYNC_OPENGL_VBO") endif () option(CONFIG_XINERAMA "Enable additional Xinerama features" ON) if (CONFIG_XINERAMA) add_definitions("-DCONFIG_XINERAMA") endif () option(CONFIG_DBUS "Enable D-Bus support" ON) if (CONFIG_DBUS) add_definitions("-DCONFIG_DBUS") list(APPEND compton_SRCS src/dbus.c) endif () option(CONFIG_XSYNC "Enable X Sync support (X Sync fence)" ON) if (CONFIG_XSYNC) add_definitions("-DCONFIG_XSYNC") endif () option(CONFIG_C2 "Enable matching system" ON) if (CONFIG_C2) add_definitions("-DCONFIG_C2") list(APPEND compton_SRCS src/c2.c) endif () add_executable(compton ${compton_SRCS}) # == Find libraries == target_link_libraries(compton "-lm" "-lrt") if (CONFIG_VSYNC_OPENGL) target_link_libraries(compton "-lGL") endif () include(FindPkgConfig) # --- Find X11 libs --- set(X11_FIND_REQUIRED 1) include(FindX11) macro(X11LIB_CHK lib) if (NOT X11_${lib}_FOUND) message(FATAL_ERROR "Could not find lib${lib}.") endif () target_link_libraries(compton ${X11_${lib}_LIB}) endmacro () target_link_libraries(compton ${X11_X11_LIB}) X11LIB_CHK(Xcomposite) X11LIB_CHK(Xdamage) X11LIB_CHK(Xext) X11LIB_CHK(Xfixes) X11LIB_CHK(Xrender) X11LIB_CHK(Xrandr) if (CONFIG_XINERAMA) X11LIB_CHK(Xinerama) endif () # --- Find libpcre --- if (CONFIG_REGEX_PCRE) pkg_check_modules(LIBPCRE REQUIRED libpcre>=8.12) add_definitions(${LIBPCRE_CFLAGS}) target_link_libraries(compton ${LIBPCRE_LDFLAGS}) endif () # --- Find libconfig --- if (CONFIG_LIBCONFIG) pkg_check_modules(LIBCONFIG REQUIRED libconfig>=1.3.2) add_definitions(${LIBCONFIG_CFLAGS}) target_link_libraries(compton ${LIBCONFIG_LDFLAGS}) if (LIBCONFIG_VERSION VERSION_LESS 1.4) add_definitions(-DCONFIG_LIBCONFIG_LEGACY) message(STATUS "libconfig-1.3* detected. Enable legacy mode.") endif () endif () # --- Find libdbus --- if (CONFIG_DBUS) pkg_check_modules(DBUS REQUIRED dbus-1) add_definitions(${DBUS_CFLAGS}) target_link_libraries(compton ${DBUS_LDFLAGS}) endif () # --- Find libdrm --- if (CONFIG_VSYNC_DRM) pkg_check_modules(LIBDRM REQUIRED libdrm) # We only use its header file add_definitions(${LIBDRM_CFLAGS}) endif () # == Install == include(GNUInstallDirs) install(TARGETS compton DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT prog) install(FILES "${PROJECT_SOURCE_DIR}/bin/compton-trans" DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT prog) install(FILES "${PROJECT_SOURCE_DIR}/README.md" "${PROJECT_SOURCE_DIR}/LICENSE" DESTINATION "${CMAKE_INSTALL_DOCDIR}" COMPONENT doc) # == CPack == if (NOT CPACK_SYSTEM_NAME) set(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_PROCESSOR}") endif () set(CPACK_PACKAGE_DESCRIPTION "A lightweight X compositing window manager, fork of xcompmgr-dana.") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A lightweight X compositing window manager") set(CPACK_PACKAGE_CONTACT "nobody ") set(CPACK_PACKAGE_DIRECTORY "${CMAKE_SOURCE_DIR}/release") set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/desc.txt") # --- Package config --- set(CPACK_GENERATOR "TBZ2" "DEB" "RPM") set(CPACK_MONOLITHIC_INSTALL 1) set(CPACK_STRIP_FILES 1) set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") set(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README.md") # --- Source package config --- set(CPACK_SOURCE_GENERATOR "TBZ2" "DEB" "RPM") set(CPACK_SOURCE_IGNORE_FILES "/\\\\." "\\\\.bak$" "\\\\.o$" "\\\\~$" "\\\\.plist$" "/compton$" # Generated package files "\\\\.tar\\\\.gz$" "\\\\.tar\\\\.bz2$" "\\\\.deb$" "\\\\.rpm$" "compton-.*\\\\.sh$" # CMake files "/Makefile$" "/CMakeFiles/" "\\\\.cmake$" "CMakeCache\\\\.txt$" "/build/" "/release/" "\\\\.diff$" "/oprofile_data/" "/install_manifest\\\\.txt$" ) # --- DEB package config --- set(CPACK_DEBIAN_PACKAGE_SECTION "x11") 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") # --- RPM package config --- set(CPACK_RPM_PACKAGE_LICENSE "unknown") 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") include(CPack) compton-0.1~beta2+20150922/bin/000077500000000000000000000000001260012102400156015ustar00rootroot00000000000000compton-0.1~beta2+20150922/bin/compton-convgen.py000077500000000000000000000073111260012102400212740ustar00rootroot00000000000000#! /usr/bin/env python3 # -*- coding: utf-8 -*- # vim:fileencoding=utf-8 import math, argparse class CGError(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) class CGBadArg(CGError): pass class CGInternal(CGError): pass def mbuild(width, height): """Build a NxN matrix filled with 0.""" result = list() for i in range(height): result.append(list()) for j in range(width): result[i].append(0.0) return result def mdump(matrix): """Dump a matrix in natural format.""" for col in matrix: print("[ ", end = ''); for ele in col: print(format(ele, "13.6g") + ", ", end = " ") print("],") def mdumpcompton(matrix): """Dump a matrix in compton's format.""" width = len(matrix[0]) height = len(matrix) print("{},{},".format(width, height), end = '') for i in range(height): for j in range(width): if int(height / 2) == i and int(width / 2) == j: continue; print(format(matrix[i][j], ".6f"), end = ",") print() def mnormalize(matrix): """Scale a matrix according to the value in the center.""" width = len(matrix[0]) height = len(matrix) factor = 1.0 / matrix[int(height / 2)][int(width / 2)] if 1.0 == factor: return matrix for i in range(height): for j in range(width): matrix[i][j] *= factor return matrix def mmirror4(matrix): """Do a 4-way mirroring on a matrix from top-left corner.""" width = len(matrix[0]) height = len(matrix) for i in range(height): for j in range(width): x = min(i, height - 1 - i) y = min(j, width - 1 - j) matrix[i][j] = matrix[x][y] return matrix def gen_gaussian(width, height, factors): """Build a Gaussian blur kernel.""" if width != height: raise CGBadArg("Cannot build an uneven Gaussian blur kernel.") size = width sigma = float(factors.get('sigma', 0.84089642)) result = mbuild(size, size) for i in range(int(size / 2) + 1): for j in range(int(size / 2) + 1): diffx = i - int(size / 2); diffy = j - int(size / 2); result[i][j] = 1.0 / (2 * math.pi * sigma) * pow(math.e, - (diffx * diffx + diffy * diffy) / (2 * sigma * sigma)) mnormalize(result) mmirror4(result) return result def gen_box(width, height, factors): """Build a box blur kernel.""" result = mbuild(width, height) for i in range(height): for j in range(width): result[i][j] = 1.0 return result def gen_invalid(width, height, factors): raise CGBadArg("Unknown kernel type.") def args_readfactors(lst): """Parse the factor arguments.""" factors = dict() if lst: for s in lst: res = s.partition('=') if not res[0]: raise CGBadArg("Factor has no key.") if not res[2]: raise CGBadArg("Factor has no value.") factors[res[0]] = float(res[2]) return factors parser = argparse.ArgumentParser(description='Build a convolution kernel.') parser.add_argument('type', help='Type of convolution kernel. May be "gaussian" (factor sigma = 0.84089642) or "box".') parser.add_argument('width', type=int, help='Width of convolution kernel. Must be an odd number.') parser.add_argument('height', nargs='?', type=int, help='Height of convolution kernel. Must be an odd number. Equals to width if omitted.') parser.add_argument('-f', '--factor', nargs='+', help='Factors of the convolution kernel, in name=value format.') parser.add_argument('--dump-compton', action='store_true', help='Dump in compton format.') args = parser.parse_args() width = args.width height = args.height if not height: height = width if not (width > 0 and height > 0): raise CGBadArg("Invalid width/height.") factors = args_readfactors(args.factor) funcs = dict(gaussian = gen_gaussian, box = gen_box) matrix = (funcs.get(args.type, gen_invalid))(width, height, factors) if args.dump_compton: mdumpcompton(matrix) else: mdump(matrix) compton-0.1~beta2+20150922/bin/compton-trans000077500000000000000000000122031260012102400203310ustar00rootroot00000000000000#!/bin/sh # # compton-trans # transset in a bash script # Copyright (c) 2011-2012, Christopher Jeffrey # # Usage: # $ compton-trans [options] [+|-]opacity # By window id # $ compton-trans -w "$WINDOWID" 75 # By name # $ compton-trans -n "urxvt" 75 # By current window # $ compton-trans -c 75 # By selection # $ compton-trans 75 # $ compton-trans -s 75 # Increment current window 5% # $ compton-trans -c +5 # Delete current window's opacity # $ compton-trans -c --delete # Reset all windows # $ compton-trans --reset # "command" is a shell built-in, faster than "which" if test -z "$(command -v xprop)" -o -z "$(command -v xwininfo)"; then 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 exit 1 fi # Variables active= wprefix= window= opacity= cur= action= treeout= wid= topmost= lineno= option= v= # Workaround: replace '-5' with '~5' so as not to confuse getopts. for v in "$@"; do shift && set -- "$@" "$(echo "$v" | sed 's/^-\([0-9]\+%\?\)$/~\1/')" done # This takes into account the fact that getopts stops on # any argument it doesn't recongize or errors on. This # allows for things like `compton-trans -5` as well # as `compton-trans -c +5 -s` (contrived example). while test $# -gt 0; do # Reset option index OPTIND=1 # Read options while getopts 'scrdgn:w:o:-:' option "$@"; do if test "$option" = '-'; then case "$OPTARG" in select | current | reset | delete | get) v='' ;; name | window | opacity) eval v=\$$OPTIND OPTIND=$((OPTIND + 1)) ;; name=* | window=* | opacity=*) v=$(echo "$OPTARG" | sed 's/^[^=]\+=//') ;; *) echo "$0: illegal option $OPTARG" >& 2 exit 1 ;; esac option=$(echo "$OPTARG" | cut -c 1) OPTARG=$v fi case "$option" in s) wprefix=''; window='' ;; c) active=$(xprop -root -notype _NET_ACTIVE_WINDOW \ | grep -Eo '0x[[:xdigit:]]+' | head -n 1) wprefix='-id'; window=$active ;; r) action='reset' ;; d) action='delete' ;; g) action='get' ;; n) wprefix='-name'; window=$OPTARG ;; w) wprefix='-id'; window=$OPTARG ;; o) opacity=$OPTARG ;; \?) exit 1 ;; esac done # Read positional arguments shift $((OPTIND - 1)) test -n "$1" && opacity=$1 && shift done # clean up opacity. xargs == a poor man's trim. opacity=$(echo "$opacity" | xargs | sed 's/%//g' | sed 's/^~\([0-9]\+\)$/-\1/') # Validate opacity value if test -z "$action" && ! echo "$opacity" | grep -q '^[+-]\?[0-9]\+$'; then echo "Invalid opacity specified: $opacity." exit 1 fi # Reset opacity for all windows if test x"$action" = x'reset'; then xwininfo -root -tree \ | sed -n 's/^ \(0x[[:xdigit:]]*\).*/\1/p' \ | while IFS=$'\n' read wid; do xprop -id "$wid" -remove _NET_WM_WINDOW_OPACITY done exit 0 fi # Get ID of the target window if test -z "$wprefix"; then treeout=$(xwininfo -children -frame) else test "$wprefix" = '-id' \ && ! echo "$window" | grep -Eiq '^[[:space:]]*(0x[[:xdigit:]]+|[[:digit:]]+)[[:space:]]*$' \ && echo 'Bad window ID.' && exit 1 treeout=$(xwininfo -children $wprefix "$window") fi wid=$(echo "$treeout" | sed -n 's/^xwininfo:.*: \(0x[[:xdigit:]]*\).*$/\1/p') if test -z "$wid"; then echo 'Failed to find window.' exit 1 fi # Make sure it's not root window if echo "$treeout" | fgrep -q 'Parent window id: 0x0'; then echo 'Cannot set opacity on root window.' exit 1 fi # If it's already the topmost window if echo "$treeout" | grep -q 'Parent window id: 0x[[:xdigit:]]* (the root window)'; then topmost=$wid else # Get the whole window tree treeout=$(xwininfo -root -tree) if test -z "$treeout"; then echo 'Failed to get root window tree.' exit 1 fi # Find the line number of the target window in the window tree lineno=$(echo -n "$treeout" | grep -nw "$wid" | head -n1 | cut -d ':' -f 1) if test -z "$lineno"; then echo 'Failed to find window in window tree.' exit 1 fi # Find the highest ancestor of the target window below topmost=$(echo -n "$treeout" \ | head -n $((lineno + 1)) \ | sed -n 's/^ \(0x[[:xdigit:]]*\).*/\1/p' \ | tail -n 1) fi if test -z "$topmost"; then echo 'Failed to find the highest parent window below root of the' \ 'selected window.' exit 1 fi # Remove the opacity property. if test x"$action" = x'delete'; then xprop -id "$topmost" -remove _NET_WM_WINDOW_OPACITY exit 0 fi # Get current opacity. cur=$(xprop -id "$topmost" -notype _NET_WM_WINDOW_OPACITY \ | sed 's/^.*\b\([0-9]\+\).*$\|^.*$/\1/') test -z "$cur" && cur=0xffffffff cur=$((cur * 100 / 0xffffffff)) # Output current opacity. if test x"$action" = x'get'; then echo "$cur" exit 0 fi # Calculate the desired opacity if echo "$opacity" | grep -q '^[+-]'; then opacity=$((cur + opacity)) fi test $opacity -lt 0 && opacity=0 test $opacity -gt 100 && opacity=100 # Set opacity opacity=$((opacity * 0xffffffff / 100)) xprop -id "$topmost" -f _NET_WM_WINDOW_OPACITY 32c \ -set _NET_WM_WINDOW_OPACITY "$opacity" compton-0.1~beta2+20150922/compton-default-fshader-win.glsl000066400000000000000000000003511260012102400232210ustar00rootroot00000000000000uniform float opacity; uniform bool invert_color; uniform sampler2D tex; void main() { vec4 c = texture2D(tex, gl_TexCoord[0]); if (invert_color) c = vec4(vec3(c.a, c.a, c.a) - vec3(c), c.a); c *= opacity; gl_FragColor = c; } compton-0.1~beta2+20150922/compton-fake-transparency-fshader-win.glsl000066400000000000000000000007471260012102400252230ustar00rootroot00000000000000uniform float opacity; uniform bool invert_color; uniform sampler2D tex; void main() { vec4 c = texture2D(tex, gl_TexCoord[0]); { // Change vec4(1.0, 1.0, 1.0, 1.0) to your desired color vec4 vdiff = abs(vec4(1.0, 1.0, 1.0, 1.0) - c); float diff = max(max(max(vdiff.r, vdiff.g), vdiff.b), vdiff.a); // Change 0.8 to your desired opacity if (diff < 0.001) c *= 0.8; } if (invert_color) c = vec4(vec3(c.a, c.a, c.a) - vec3(c), c.a); c *= opacity; gl_FragColor = c; } compton-0.1~beta2+20150922/compton.desktop000066400000000000000000000003301260012102400200770ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Name=compton GenericName=X compositor Comment=A X compositor Categories=Utility; TryExec=compton Exec=compton Icon=compton # Thanks to quequotion for providing this file! compton-0.1~beta2+20150922/compton.sample.conf000066400000000000000000000040461260012102400206430ustar00rootroot00000000000000# Shadow shadow = true; no-dnd-shadow = true; no-dock-shadow = true; clear-shadow = true; shadow-radius = 7; shadow-offset-x = -7; shadow-offset-y = -7; # shadow-opacity = 0.7; # shadow-red = 0.0; # shadow-green = 0.0; # shadow-blue = 0.0; shadow-exclude = [ "name = 'Notification'", "class_g = 'Conky'", "class_g ?= 'Notify-osd'", "class_g = 'Cairo-clock'", "_GTK_FRAME_EXTENTS@:c" ]; # shadow-exclude = "n:e:Notification"; # shadow-exclude-reg = "x10+0+0"; # xinerama-shadow-crop = true; # Opacity menu-opacity = 0.8; inactive-opacity = 0.8; # active-opacity = 0.8; frame-opacity = 0.7; inactive-opacity-override = false; alpha-step = 0.06; # inactive-dim = 0.2; # inactive-dim-fixed = true; # blur-background = true; # blur-background-frame = true; blur-kern = "3x3box" # 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" # blur-background-fixed = true; blur-background-exclude = [ "window_type = 'dock'", "window_type = 'desktop'", "_GTK_FRAME_EXTENTS@:c" ]; # opacity-rule = [ "80:class_g = 'URxvt'" ]; # Fading fading = true; # fade-delta = 30; fade-in-step = 0.03; fade-out-step = 0.03; # no-fading-openclose = true; # no-fading-destroyed-argb = true; fade-exclude = [ ]; # Other backend = "xrender" mark-wmwin-focused = true; mark-ovredir-focused = true; # use-ewmh-active-win = true; detect-rounded-corners = true; detect-client-opacity = true; refresh-rate = 0; vsync = "none"; dbe = false; paint-on-overlay = true; # sw-opti = true; # unredir-if-possible = true; # unredir-if-possible-delay = 5000; # unredir-if-possible-exclude = [ ]; focus-exclude = [ "class_g = 'Cairo-clock'" ]; detect-transient = true; detect-client-leader = true; invert-color-include = [ ]; # resize-damage = 1; # GLX backend # glx-no-stencil = true; glx-copy-from-front = false; # glx-use-copysubbuffermesa = true; # glx-no-rebind-pixmap = true; glx-swap-method = "undefined"; # glx-use-gpushader4 = true; # xrender-sync = true; # xrender-sync-fence = true; # Window type settings wintypes: { tooltip = { fade = true; shadow = true; opacity = 0.75; focus = true; }; }; compton-0.1~beta2+20150922/dbus-examples/000077500000000000000000000000001260012102400176025ustar00rootroot00000000000000compton-0.1~beta2+20150922/dbus-examples/cdbus-driver.sh000077500000000000000000000040651260012102400225370ustar00rootroot00000000000000#!/bin/sh if [ -z "$SED" ]; then SED="sed" command -v gsed > /dev/null && SED="gsed" fi # === Get connection parameters === dpy=$(echo -n "$DISPLAY" | tr -c '[:alnum:]' _) if [ -z "$dpy" ]; then echo "Cannot find display." exit 1 fi service="com.github.chjj.compton.${dpy}" interface='com.github.chjj.compton' object='/com/github/chjj/compton' type_win='uint32' type_enum='uint16' # === DBus methods === # List all window ID compton manages (except destroyed ones) dbus-send --print-reply --dest="$service" "$object" "${interface}.list_win" # Ensure we are tracking focus dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_set" string:track_focus boolean:true # Get window ID of currently focused window focused=$(dbus-send --print-reply --dest="$service" "$object" "${interface}.find_win" string:focused | $SED -n 's/^[[:space:]]*'${type_win}'[[:space:]]*\([[:digit:]]*\).*/\1/p') if [ -n "$focused" ]; then # Get invert_color_force property of the window dbus-send --print-reply --dest="$service" "$object" "${interface}.win_get" "${type_win}:${focused}" string:invert_color_force # Set the window to have inverted color dbus-send --print-reply --dest="$service" "$object" "${interface}.win_set" "${type_win}:${focused}" string:invert_color_force "${type_enum}:1" else echo "Cannot find focused window." fi # Set the clear_shadow setting to true dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_set" string:clear_shadow boolean:true # Get the clear_shadow setting dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_get" string:clear_shadow # Reset compton sleep 3 dbus-send --print-reply --dest="$service" "$object" "${interface}.reset" # Undirect window sleep 3 dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_set" string:redirected_force uint16:0 # Revert back to auto sleep 3 dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_set" string:redirected_force uint16:2 # Force repaint dbus-send --print-reply --dest="$service" "$object" "${interface}.repaint" compton-0.1~beta2+20150922/dbus-examples/inverter.sh000077500000000000000000000044061260012102400220030ustar00rootroot00000000000000#!/bin/sh # === Verify `compton --dbus` status === if [ -z "`dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep compton`" ]; then echo "compton DBus interface unavailable" if [ -n "`pgrep compton`" ]; then echo "compton running without dbus interface" #killall compton & # Causes all windows to flicker away and come back ugly. #compton --dbus & # Causes all windows to flicker away and come back beautiful else echo "compton not running" fi exit 1; fi # === Setup sed === if [ -z "$SED" ]; then SED="sed" command -v gsed > /dev/null && SED="gsed" fi # === Get connection parameters === dpy=$(echo -n "$DISPLAY" | tr -c '[:alnum:]' _) if [ -z "$dpy" ]; then echo "Cannot find display." exit 1; fi service="com.github.chjj.compton.${dpy}" interface="com.github.chjj.compton" compton_dbus="dbus-send --print-reply --dest="${service}" / "${interface}"." type_win='uint32' type_enum='uint16' # === Color Inversion === # Get window ID of window to invert if [ -z "$1" -o "$1" = "selected" ]; then window=$(xwininfo -frame | sed -n 's/^xwininfo: Window id: \(0x[[:xdigit:]][[:xdigit:]]*\).*/\1/p') # Select window by mouse elif [ "$1" = "focused" ]; then # Ensure we are tracking focus ${compton_dbus}opts_set string:track_focus boolean:true & window=$(${compton_dbus}find_win string:focused | $SED -n 's/^[[:space:]]*'${type_win}'[[:space:]]*\([[:digit:]]*\).*/\1/p') # Query compton for the active window elif echo "$1" | grep -Eiq '^([[:digit:]][[:digit:]]*|0x[[:xdigit:]][[:xdigit:]]*)$'; then window="$1" # Accept user-specified window-id if the format is correct else echo "$0" "[ selected | focused | window-id ]" fi # Color invert the selected or focused window if [ -n "$window" ]; then invert_status="$(${compton_dbus}win_get "${type_win}:${window}" string:invert_color | $SED -n 's/^[[:space:]]*boolean[[:space:]]*\([[:alpha:]]*\).*/\1/p')" if [ "$invert_status" = true ]; then invert=0 # Set the window to have normal color else invert=1 # Set the window to have inverted color fi ${compton_dbus}win_set "${type_win}:${window}" string:invert_color_force "${type_enum}:${invert}" & else echo "Cannot find $1 window." exit 1; fi exit 0; compton-0.1~beta2+20150922/desc.txt000066400000000000000000000001021260012102400165010ustar00rootroot00000000000000Compton is a X compositing window manager, fork of xcompmgr-dana. compton-0.1~beta2+20150922/functions.sh000077500000000000000000000020261260012102400174000ustar00rootroot00000000000000#!/bin/bash # Shared functions for various supporting scripts of compton # Mostly copied from Gentoo gentoo-functions GOOD=$'\e[32;01m' WARN=$'\e[33;01m' BAD=$'\e[31;01m' HILITE=$'\e[36;01m' BRACKET=$'\e[34;01m' NORMAL=$'\e[0m' # @FUNCTION: eerror # @USAGE: [message] # @DESCRIPTION: # Show an error message. eerror() { echo -e "$@" | while read -r ; do echo " $BAD*$NORMAL $REPLY" >&2 done return 0 } # @FUNCTION: einfo # @USAGE: [message] # @DESCRIPTION: # Show a message. einfo() { echo -e "$@" | while read -r ; do echo " $GOOD*$NORMAL $REPLY" done return 0 } # @FUNCTION: die # @USAGE: # @DESCRIPTION: # Print the call stack and the working directory, then quit the shell. die() { eerror "Call stack:" (( n = ${#FUNCNAME[@]} - 1 )) while (( n > 0 )); do funcname=${FUNCNAME[$((n - 1))]} sourcefile=$(basename ${BASH_SOURCE[${n}]}) lineno=${BASH_LINENO[$((n - 1))]} eerror " ${sourcefile}:${lineno} - Called ${funcname}()" (( n-- )) done eerror "Working directory: '$(pwd)'" exit 1 } compton-0.1~beta2+20150922/make-release.sh000077500000000000000000000011401260012102400177170ustar00rootroot00000000000000#!/bin/bash # Make a release source tarball containing pre-built documentation BASE_DIR=$(dirname "$0") . "${BASE_DIR}/functions.sh" main() { TMP=/tmp mkdir -p "${TMP}" VER="$(make version)" P="compton-${VER}" git archive --format=tar -o "${TMP}/${P}.tar" --prefix="${P}/" HEAD || die cd "${TMP}" || die tar xf "${TMP}/${P}.tar" || die sed -i "s/\(COMPTON_VERSION ?=\).*/\1 ${VER}/" "${P}/Makefile" || die cd "${P}" || die make docs || die cd .. || die tar cJf "${P}.tar.xz" "${P}" || die rm -r "${P}" "${P}.tar" || die einfo Archive is now on $(realpath ${P}.tar.xz) } main compton-0.1~beta2+20150922/man/000077500000000000000000000000001260012102400156045ustar00rootroot00000000000000compton-0.1~beta2+20150922/man/CMakeLists.txt000066400000000000000000000026011260012102400203430ustar00rootroot00000000000000option(NEW_DOC "Build new man pages and HTML documentation" ON) # == Build documentation == # Stolen from https://issues.apache.org/jira/secure/attachment/12455612/AVRO-470.patch if (NEW_DOC) set (MAN_SRC compton.1.asciidoc compton-trans.1.asciidoc ) find_program(ASCIIDOC_EXEC asciidoc) find_program(ASCIIDOC_A2X_EXEC a2x) if (ASCIIDOC_EXEC AND ASCIIDOC_A2X_EXEC) foreach(_file ${MAN_SRC}) # get_filename_component() does not handle ".1.asciidoc" # correctly string(REPLACE ".asciidoc" "" _file_we "${_file}") set(_file_path "${CMAKE_CURRENT_SOURCE_DIR}/${_file}") set(_html_out "${_file_we}.html") set(_man_out "${_file_we}") add_custom_target(compton_man_${_file_we} ALL COMMAND ${ASCIIDOC_A2X_EXEC} --format manpage "${_file_path}" DEPENDS "${_file_path}" ) add_custom_command( OUTPUT "${_html_out}" COMMAND ${ASCIIDOC_EXEC} -o "${_html_out}" "${_file_path}" DEPENDS "${_file_path}" ) add_custom_target(compton_html_${_file_we} ALL DEPENDS "${_html_out}" ) endforeach(_file) else(ASCIIDOC_EXEC AND ASCIIDOC_A2X_EXEC) message(WARNING "asciidoc/a2x not found. New man pages and HTML documentation will not be built.") endif(ASCIIDOC_EXEC AND ASCIIDOC_A2X_EXEC) endif(NEW_DOC) # == Install == include(GNUInstallDirs) install(FILES "compton.1" "compton-trans.1" DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" COMPONENT doc) compton-0.1~beta2+20150922/man/compton-trans.1.asciidoc000066400000000000000000000037441260012102400222570ustar00rootroot00000000000000compton-trans(1) ================ :doctype: manpage :man source: compton :man version: nightly-20121114 :man manual: LOCAL USER COMMANDS NAME ---- compton-trans - an opacity setter tool SYNOPSIS -------- *compton-trans* [-w 'WINDOW_ID'] [-n 'WINDOW_NAME'] [-c] [-s] 'OPACITY' DESCRIPTION ----------- *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). OPTIONS ------- *-w* 'WINDOW_ID':: Specify the window id of the target window. *-n* 'WINDOW_NAME':: Specify and try to match a window name. *-c*:: Specify the currently active window as target. Only works if EWMH '_NET_ACTIVE_WINDOW' property exists on root window. *-s*:: Select target window with mouse cursor. This is the default if no window has been specified. *-o* 'OPACITY':: 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. EXAMPLES -------- * Set the opacity of the window with specific window ID to 75%: + ------------ compton-trans -w "$WINDOWID" 75 ------------ * Set the opacity of the window with the name "urxvt" to 75%: + ------------ compton-trans -n "urxvt" 75 ------------ * Set current window to opacity of 75%: + ------------ compton-trans -c 75 ------------ * Select target window and set opacity to 75%: + ------------ compton-trans -s 75 ------------ * Increment opacity of current active window by 5%: + ------------ compton-trans -c +5 ------------ * Decrement opacity of current active window by 5%: + ------------ compton-trans -c -- -5 ------------ BUGS ---- Please report any bugs you find to . AUTHORS ------- Christopher Jeffrey (). SEE ALSO -------- link:compton.1.html[*compton*(1)], *xprop*(1), *xwininfo*(1) compton-0.1~beta2+20150922/man/compton.1.asciidoc000066400000000000000000000554751260012102400211420ustar00rootroot00000000000000compton(1) ========== :doctype: manpage :man source: compton :man version: nightly-20141124 :man manual: LOCAL USER COMMANDS NAME ---- compton - a compositor for X11 SYNOPSIS -------- *compton* ['OPTIONS'] WARNING ------- This man page may be less up-to-date than the usage text in compton (`compton -h`). DESCRIPTION ----------- 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. OPTIONS ------- *-h*, *--help*:: Get the usage text embedded in program code, which may be more up-to-date than this man page. *-d* 'DISPLAY':: Display to be managed. *-r*, *--shadow-radius*='RADIUS':: The blur radius for shadows, in pixels. (defaults to 12) *-o*, *--shadow-opacity*='OPACITY':: The opacity of shadows. (0.0 - 1.0, defaults to 0.75) *-l*, *--shadow-offset-x*='OFFSET':: The left offset for shadows, in pixels. (defaults to -15) *-t*, *--shadow-offset-y*='OFFSET':: The top offset for shadows, in pixels. (defaults to -15) *-I*, *--fade-in-step*='OPACITY_STEP':: Opacity change between steps while fading in. (0.01 - 1.0, defaults to 0.028) *-O*, *--fade-out-step*='OPACITY_STEP':: Opacity change between steps while fading out. (0.01 - 1.0, defaults to 0.03) *-D*, *--fade-delta*='MILLISECONDS':: The time between steps in fade step, in milliseconds. (> 0, defaults to 10) *-m*, *--menu-opacity*='OPACITY':: Default opacity for dropdown menus and popup menus. (0.0 - 1.0, defaults to 1.0) *-c*, *--shadow*:: Enabled client-side shadows on windows. Note desktop windows (windows with '_NET_WM_WINDOW_TYPE_DESKTOP') never get shadow. *-C*, *--no-dock-shadow*:: Avoid drawing shadows on dock/panel windows. *-z*, *--clear-shadow*:: Zero the part of the shadow's mask behind the window. Note this may not work properly on ARGB windows with fully transparent areas. *-f*, *--fading*:: Fade windows in/out when opening/closing and when opacity changes, unless *--no-fading-openclose* is used. *-F*:: Equals to *-f*. Deprecated. *-i*, *--inactive-opacity*='OPACITY':: Opacity of inactive windows. (0.1 - 1.0, disabled by default) *-e*, *--frame-opacity*='OPACITY':: Opacity of window titlebars and borders. (0.1 - 1.0, disabled by default) *-G*, *--no-dnd-shadow*:: Don't draw shadows on drag-and-drop windows. *-b*, *--daemon*:: Daemonize process. Fork to background after initialization. Causes issues with certain (badly-written) drivers. *-S*:: Enable synchronous X operation (for debugging). *--show-all-xerrors*:: Show all X errors (for debugging). *--config* 'PATH':: 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. *--write-pid-path* 'PATH':: Write process ID to a file. *--shadow-red* 'VALUE':: Red color value of shadow (0.0 - 1.0, defaults to 0). *--shadow-green* 'VALUE':: Green color value of shadow (0.0 - 1.0, defaults to 0). *--shadow-blue* 'VALUE':: Blue color value of shadow (0.0 - 1.0, defaults to 0). *--inactive-opacity-override*:: Let inactive opacity set by *-i* overrides the windows' '_NET_WM_OPACITY' values. *--active-opacity* 'OPACITY':: Default opacity for active windows. (0.0 - 1.0) *--inactive-dim* 'VALUE':: Dim inactive windows. (0.0 - 1.0, defaults to 0.0) *--mark-wmwin-focused*:: Try to detect WM windows (a non-override-redirect window with no child that has `WM_STATE`) and mark them as active. *--mark-ovredir-focused*:: Mark override-redirect windows that doesn't have a child window with `WM_STATE` focused. *--no-fading-openclose*:: Do not fade on window open/close. *--no-fading-destroyed-argb*:: Do not fade destroyed ARGB windows with WM frame. Workaround of bugs in Openbox, Fluxbox, etc. *--shadow-ignore-shaped*:: 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. *--detect-rounded-corners*:: Try to detect windows with rounded corners and don't consider them shaped windows. The accuracy is not very high, unfortunately. *--detect-client-opacity*:: Detect '_NET_WM_OPACITY' on client windows, useful for window managers not passing '_NET_WM_OPACITY' of client windows to frame windows. *--refresh-rate* 'REFRESH_RATE':: Specify refresh rate of the screen. If not specified or 0, compton will try detecting this with X RandR extension. *--vsync* 'VSYNC_METHOD':: Set VSync method. VSync methods currently available: + -- * 'none': No VSync * 'drm': VSync with 'DRM_IOCTL_WAIT_VBLANK'. May only work on some (DRI-based) drivers. * 'opengl': Try to VSync with 'SGI_video_sync' OpenGL extension. Only work on some drivers. * 'opengl-oml': Try to VSync with 'OML_sync_control' OpenGL extension. Only work on some drivers. * '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. * 'opengl-mswc': Try to VSync with 'MESA_swap_control' OpenGL extension. Basically the same as 'opengl-swc' above, except the extension we use. (Note some VSync methods may not be enabled at compile time.) -- *--vsync-aggressive*:: 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. *--alpha-step* 'VALUE':: X Render backend: Step for pregenerating alpha pictures. (0.01 - 1.0, defaults to 0.03) *--dbe*:: Enable DBE painting mode, intended to use with VSync to (hopefully) eliminate tearing. Reported to have no effect, though. *--paint-on-overlay*:: Painting on X Composite overlay window instead of on root window. *--sw-opti*:: 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. *--use-ewmh-active-win*:: 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. *--respect-prop-shadow*:: Respect '_COMPTON_SHADOW'. This a prototype-level feature, which you must not rely on. *--unredir-if-possible*:: 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. *--unredir-if-possible-delay* 'MILLISECONDS':: Delay before unredirecting the window, in milliseconds. Defaults to 0. *--unredir-if-possible-exclude* 'CONDITION':: Conditions of windows that shouldn't be considered full-screen for unredirecting screen. *--shadow-exclude* 'CONDITION':: Specify a list of conditions of windows that should have no shadow. *--fade-exclude* 'CONDITION':: Specify a list of conditions of windows that should not be faded. *--focus-exclude* 'CONDITION':: Specify a list of conditions of windows that should always be considered focused. *--inactive-dim-fixed*:: Use fixed inactive dim value, instead of adjusting according to window opacity. *--detect-transient*:: Use 'WM_TRANSIENT_FOR' to group windows, and consider windows in the same group focused at the same time. *--detect-client-leader*:: 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. *--blur-background*:: Blur background of semi-transparent / ARGB windows. Bad in performance, with driver-dependent behavior. The name of the switch may change without prior notifications. *--blur-background-frame*:: 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. *--blur-background-fixed*:: Use fixed blur strength rather than adjusting according to window opacity. *--blur-kern* 'MATRIX':: Specify the blur convolution kernel, with the following format: + ---- WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5... ---- + 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. + A 7x7 Gaussian blur kernel (sigma = 0.84089642) looks like: + ---- --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' ---- + 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. *--blur-background-exclude* 'CONDITION':: Exclude conditions for background blur. *--resize-damage* 'INTEGER':: 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. *--invert-color-include* 'CONDITION':: Specify a list of conditions of windows that should be painted with inverted color. Resource-hogging, and is not well tested. *--opacity-rule* 'OPACITY':'CONDITION':: 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. *--shadow-exclude-reg* 'GEOMETRY':: 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. *--xinerama-shadow-crop*:: Crop shadow of a window fully on a particular Xinerama screen to the screen. *--backend* 'BACKEND':: Specify the backend to use: `xrender`, `glx`, or `xr_glx_hybrid`. `xrender` is the default one. + -- * `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. * `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. * `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. -- *--glx-no-stencil*:: 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. *--glx-copy-from-front*:: 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. *--glx-use-copysubbuffermesa*:: 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*. *--glx-no-rebind-pixmap*:: 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. *--glx-swap-method* undefined/exchange/copy/3/4/5/6/buffer-age:: 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`. *--glx-use-gpushader4*:: GLX backend: Use 'GL_EXT_gpu_shader4' for some optimization on blur GLSL code. My tests on GTX 670 show no noticeable effect. *--xrender-sync*:: Attempt to synchronize client applications' draw calls with `XSync()`, used on GLX backend to ensure up-to-date window content is painted. *--xrender-sync-fence*:: 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`. *--glx-fshader-win* 'SHADER':: 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. *--force-win-blend*:: Force all windows to be painted with blending. Useful if you have a *--glx-fshader-win* that could turn opaque pixels transparent. *--dbus*:: Enable remote control via D-Bus. See the *D-BUS API* section below for more details. *--benchmark* 'CYCLES':: Benchmark mode. Repeatedly paint until reaching the specified cycles. *--benchmark-wid* 'WINDOW_ID':: Specify window ID to repaint in benchmark mode. If omitted or is 0, the whole screen is repainted. FORMAT OF CONDITIONS -------------------- Some options accept a condition string to match certain windows. A condition string is formed by one or more conditions, joined by logical operators. A condition with "exists" operator looks like this: [] : With equals operator it looks like: [] : = With greater-than/less-than operators it looks like: [] : 'NEGATION' (optional) is one or more exclamation marks; '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`. 'CLIENT/FRAME' is a single `@` if the window attribute should be be looked up on client window, nothing if on frame window; '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. '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. '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. 'OP QUALIFIER' (optional), applicable only for equals operator, could be `?` (ignore-case). 'MATCH TYPE' (optional), applicable only for equals operator, could be nothing (exact match), `*` (match anywhere), `^` (match from start), `%` (wildcard), or `~` (PCRE regular expression). '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). '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. Supported logical operators are `&&` (and) and `||` (or). `&&` has higher precedence than `||`, left-to-right associativity. Use parentheses to change precedence. Examples: # If the window is focused focused focused = 1 # If the window is not override-redirected !override_redirect override_redirect = false override_redirect != true override_redirect != 1 # If the window is a menu window_type *= "menu" _NET_WM_WINDOW_TYPE@:a *= "MENU" # If the window name contains "Firefox", ignore case name *?= "Firefox" _NET_WM_NAME@:s *?= "Firefox" # If the window name ends with "Firefox" name %= "*Firefox" name ~= "Firefox$" # If the window has a property _COMPTON_SHADOW with value 0, type CARDINAL, # format 32, value 0, on its frame window _COMPTON_SHADOW:32c = 0 # If the third value of _NET_FRAME_EXTENTS is less than 20, or there's no # _NET_FRAME_EXTENTS property on client window _NET_FRAME_EXTENTS@[2]:32c < 20 || !_NET_FRAME_EXTENTS@:32c # The pattern here will be parsed as "dd4" name = "\x64\x64\o64" # The pattern here will be parsed as "\x64\x64\x64" name = r"\x64\x64\o64" LEGACY FORMAT OF CONDITIONS --------------------------- This is the old condition format we once used. Support of this format might be removed in the future. condition = TARGET:TYPE[FLAGS]:PATTERN 'TARGET' is one of "n" (window name), "i" (window class instance), "g" (window general class), and "r" (window role). '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). 'FLAGS' could be a series of flags. Currently the only defined flag is "i" (ignore case). 'PATTERN' is the actual pattern string. CONFIGURATION FILES ------------------- 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`). 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: ------------ wintypes: { WINDOW_TYPE = { fade = BOOL; shadow = BOOL; opacity = FLOAT; focus = BOOL; }; }; ------------ '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.) SIGNALS ------- * compton reinitializes itself upon receiving `SIGUSR1`. D-BUS API --------- 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. The D-Bus methods and signals are not yet stable, thus undocumented right now. EXAMPLES -------- * Disable configuration file parsing: + ------------ $ compton --config /dev/null ------------ * Run compton with client-side shadow and fading, disable shadow on dock windows and drag-and-drop windows: + ------------ $ compton -cCGf ------------ * 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: + ------------ $ compton -bcCGf -i 0.8 -e 0.8 --no-fading-openclose --sw-opti ------------ * Draw white shadows: + ------------ $ compton -c --shadow-red 1 --shadow-green 1 --shadow-blue 1 ------------ * Avoid drawing shadows on wbar window: + ------------ $ compton -c --shadow-exclude 'class_g = "wbar"' ------------ * Enable OpenGL SGI_swap_control VSync with GLX backend: + ------------ $ compton --backend glx --vsync opengl-swc ------------ BUGS ---- Please report any you find to . AUTHORS ------- 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. RESOURCES --------- Homepage: SEE ALSO -------- *xcompmgr*(1), link:compton-trans.html[*compton-trans*(1)] compton-0.1~beta2+20150922/media/000077500000000000000000000000001260012102400161105ustar00rootroot00000000000000compton-0.1~beta2+20150922/media/compton.svg000066400000000000000000000133441260012102400203150ustar00rootroot00000000000000 compton-0.1~beta2+20150922/media/icons/000077500000000000000000000000001260012102400172235ustar00rootroot00000000000000compton-0.1~beta2+20150922/media/icons/48x48/000077500000000000000000000000001260012102400200225ustar00rootroot00000000000000compton-0.1~beta2+20150922/media/icons/48x48/compton.png000066400000000000000000000046171260012102400222170ustar00rootroot00000000000000‰PNG  IHDR00Wù‡tEXtSoftwareAdobe ImageReadyqÉe< 1IDATxÚìY{lÇÿv÷|gŸíóÙ`ül\R—PÊ‘„„¢¢*‰¢¤4V¢HH}R©M+AC!´‘(RDÕT©ÓHª¦qZú4X¡¥¸"­š:‚΂<|OŒ}ÇùÎç;ßÎô›}œw÷ÖwÇÙm¨ÄHŸgvovæ÷›o¾ÇŒî—ûeN…›ï·xýNÔÅc› ‚TËÂQph\žÏùøù&@Z)ÀY¸3øœ–äè­­›6mâïu«‰\+Â¥Û`³ºð¯ý^'àÒ˜!Å%+ÏÀã>¿ë Ÿc¶>"ÀÆL2 j³=šM»<¾»=>×ň¿êó·¢Av c'Ê›øjß©%‹%ƒü†Ïß„ïZQ¶ðT6`­ñꄈ>žçÿŽÿúí¥KÜìûí3ü½øÍvìÆö¶_4.éœ7ü­¥0CøñœôYloUß ²·¬$Ð;)íP#i¾‰Ó|£xoÎÖù­Øé¸ˆÒYéå.I¯é«%©Ôó¯aÙûxØ`Ûá ÈT ÿk¥xü/¯<7FK68¦ Рʀ’'ápx$1«ÆJŠá+64Á"‹ªFS"ÜSàINÃ…øô'šõ£MAv pœ#îõ>€±£÷ĉ‰»&p~qÃGküf°Ûs‘x²Ôßr8 F2Æ©¶’¬@bO•—Ám$t$7’ÉF"q'|Ø÷Ö¡OçdH€iષI«bu;9x^©¬„ ¸òw[Î#wÇC0EHævÅÀèï×ëí½«?Vp¸°¸!ŒãíPý¹¨,kÛ|[õ‚À³ò~÷ƒ éi]Ä&¸BSÁ@;‚Í>ï@&¢+#QŠàƒà—ã^/´Ä’Ix÷Ã!~î,TJGB¨©y«Ð¼Ä•þ@H `i5ﬨ€Íe¥¦ý»úû¡k`.ƒÒLÖ55Iòµ––4ø½œÚv#àxÅk _ÝN‰ÞÞ'ßûú3§³aroÁ8€ÕVí»zœpUeFß(‚zõäIè¼xnMLè~ ÐO††àÒð0¬ª¯‡×Nþ †ðàX EÃ&‘; ,¬Æ’a ¥¥e ^oçÀÀ€XðBu>kÌk^t”›‚߉+Úƒ«N;1 +ñ÷ü ®#xÊ€3áåš Q'úú€ˆ¢œ;•”°ÜÉ‘÷ª œJN³Å…ë°Q9ˆè‚Qw]-”ózî‡þÝ ïãÊsJ€‚™”À œ´âiAð¼ÒVk¡¬ lË—KÏÊvb)‡¥±tjECàbÆÚ‘¡&œYT–ðKh´Fð#¸]Ž!x>í¡8ibbP1Õ€gà@¢ ê3j3éñ€m¤¹(Ë€¥,898Ãê¨é2KƒE¥¦œÜ®3 TîàpÚµ‚†„:¦úœÞ2 ¤¦ –Ìw̵& #´ÞIœš²ad¶›sæ"ÑbÍt›#щô>7'¡o$!Õ–Ìߘk¾uSG‚f3bj8š‘˜%gÒ‰Žg~6Ĩ ‘Àth¥3'»Ùàa’ƒÄeT­±Ô–•›’`K¯¦AriBׇPHMD$4[2Gtn‰3Q€WÌtÉkêë2{J9± €­äÛµ†«ñD¼òÌkÞ³þDÓŸM Æã¸h¸KoÞÜA­6))YA¤3¬þƒK¨îI‡ÆÕ–—à «VÁQôD*xª˜& :ÊkÈðfµJ:•: ñø‹]ItܧlóTB¹·±+·ös¯ÿr_ÊQ¾M›F¼ét v{F û!²«ãcéè ‚°úÌ›1ÖR›+©}?c™Û· Eâ˜àMæ U‚.NJ§gÒÝFü“šÓh¼çtœ½•Z•5Õ‹`·k5ìu÷ÀµÉÉ4hpµ»½Ÿ´µDÀÁ‚S‰PC½mí5lÚÁ;±XFß2«ÚŸy~úØcફÓ熆ØÉZÛúuP[Ro¬}š1##ÖIoo[—9g£`ú`¯j¢màÏ ÀÊ9¤ÓQôó?ºü\›š’W¼¸8Üš<ŽÉÇbÇ…]?Þ…Ý®£ÈÜΜê^g\l ùù±1S·šo)ÃÔù¥Å @ñ¼œÂSˆ¶EP‹,váa¶ú¡làó"` Ytî`§¤ÙHtDà_Ã#ðê¹OF£xĦú¸ÓÜü“Ôw¶Xs‘ó< ¼üòÏÑ‘>Åel8ù!€þ9tã¬À,²Ü–ûêswÿù Ð18I掸ÉÐR< YÏÎó¨tV<èñüÏ´ àƒÁœ|/¤»pRïp´6aÁ­TÝÝ OTUÁÃhÀ_\¸êPƈáh †à`øüsdÜx—¼‚å,ÌëXä6î}Z[+Û€:þÈȦSkùKA÷BD¾§d¹·S0FªHᤣë×Ãi$ña+µ »»OÉéïÝÿÔörF^˜aÞ/ÿOå? ÷ s¸˜C+IEND®B`‚compton-0.1~beta2+20150922/src/000077500000000000000000000000001260012102400156205ustar00rootroot00000000000000compton-0.1~beta2+20150922/src/c2.c000066400000000000000000001054201260012102400162720ustar00rootroot00000000000000/* * Compton - a compositor for X11 * * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * * Copyright (c) 2011-2013, Christopher Jeffrey * See LICENSE for more information. * */ #include "c2.h" /** * Parse a condition string. */ c2_lptr_t * c2_parsed(session_t *ps, c2_lptr_t **pcondlst, const char *pattern, void *data) { if (!pattern) return NULL; // Parse the pattern c2_ptr_t result = C2_PTR_INIT; int offset = -1; if (strlen(pattern) >= 2 && ':' == pattern[1]) offset = c2_parse_legacy(ps, pattern, 0, &result); else offset = c2_parse_grp(ps, pattern, 0, &result, 0); if (offset < 0) { c2_freep(&result); return NULL; } // Insert to pcondlst { const static c2_lptr_t lptr_def = C2_LPTR_INIT; c2_lptr_t *plptr = malloc(sizeof(c2_lptr_t)); if (!plptr) printf_errfq(1, "(): Failed to allocate memory for new condition linked" " list element."); memcpy(plptr, &lptr_def, sizeof(c2_lptr_t)); plptr->ptr = result; plptr->data = data; if (pcondlst) { plptr->next = *pcondlst; *pcondlst = plptr; } #ifdef DEBUG_C2 printf_dbgf("(\"%s\"): ", pattern); c2_dump(plptr->ptr); #endif return plptr; } } #undef c2_error #define c2_error(format, ...) do { \ printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \ ## __VA_ARGS__); \ return -1; \ } while(0) #define C2H_SKIP_SPACES() { while (isspace(pattern[offset])) ++offset; } /** * Parse a group in condition string. * * @return offset of next character in string */ static int c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level) { // Check for recursion levels if (level > C2_MAX_LEVELS) c2_error("Exceeded maximum recursion levels."); if (!pattern) return -1; // Expected end character const char endchar = (offset ? ')': '\0'); #undef c2_error #define c2_error(format, ...) do { \ printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \ ## __VA_ARGS__); \ goto c2_parse_grp_fail; \ } while(0) // We use a system that a maximum of 2 elements are kept. When we find // the third element, we combine the elements according to operator // precedence. This design limits operators to have at most two-levels // of precedence and fixed left-to-right associativity. // For storing branch operators. ops[0] is actually unused c2_b_op_t ops[3] = { }; // For storing elements c2_ptr_t eles[2] = { C2_PTR_INIT, C2_PTR_INIT }; // Index of next free element slot in eles int elei = 0; // Pointer to the position of next element c2_ptr_t *pele = eles; // Negation flag of next operator bool neg = false; // Whether we are expecting an element immediately, is true at first, or // after encountering a logical operator bool next_expected = true; // Parse the pattern character-by-character for (; pattern[offset]; ++offset) { assert(elei <= 2); // Jump over spaces if (isspace(pattern[offset])) continue; // Handle end of group if (')' == pattern[offset]) break; // Handle "!" if ('!' == pattern[offset]) { if (!next_expected) c2_error("Unexpected \"!\"."); neg = !neg; continue; } // Handle AND and OR if ('&' == pattern[offset] || '|' == pattern[offset]) { if (next_expected) c2_error("Unexpected logical operator."); next_expected = true; if (!mstrncmp("&&", pattern + offset)) { ops[elei] = C2_B_OAND; ++offset; } else if (!mstrncmp("||", pattern + offset)) { ops[elei] = C2_B_OOR; ++offset; } else c2_error("Illegal logical operator."); continue; } // Parsing an element if (!next_expected) c2_error("Unexpected expression."); assert(!elei || ops[elei]); // If we are out of space if (2 == elei) { --elei; // If the first operator has higher or equal precedence, combine // the first two elements if (c2h_b_opcmp(ops[1], ops[2]) >= 0) { eles[0] = c2h_comb_tree(ops[1], eles[0], eles[1]); c2_ptr_reset(&eles[1]); pele = &eles[elei]; ops[1] = ops[2]; } // Otherwise, combine the second and the incoming one else { eles[1] = c2h_comb_tree(ops[2], eles[1], C2_PTR_NULL); assert(eles[1].isbranch); pele = &eles[1].b->opr2; } // The last operator always needs to be reset ops[2] = C2_B_OUNDEFINED; } // It's a subgroup if it starts with '(' if ('(' == pattern[offset]) { if ((offset = c2_parse_grp(ps, pattern, offset + 1, pele, level + 1)) < 0) goto c2_parse_grp_fail; } // Otherwise it's a leaf else { if ((offset = c2_parse_target(ps, pattern, offset, pele)) < 0) goto c2_parse_grp_fail; assert(!pele->isbranch && !c2_ptr_isempty(*pele)); if ((offset = c2_parse_op(pattern, offset, pele)) < 0) goto c2_parse_grp_fail; if ((offset = c2_parse_pattern(ps, pattern, offset, pele)) < 0) goto c2_parse_grp_fail; if (!c2_l_postprocess(ps, pele->l)) goto c2_parse_grp_fail; } // Decrement offset -- we will increment it in loop update --offset; // Apply negation if (neg) { neg = false; if (pele->isbranch) pele->b->neg = !pele->b->neg; else pele->l->neg = !pele->l->neg; } next_expected = false; ++elei; pele = &eles[elei]; } // Wrong end character? if (pattern[offset] && !endchar) c2_error("Expected end of string but found '%c'.", pattern[offset]); if (!pattern[offset] && endchar) c2_error("Expected '%c' but found end of string.", endchar); // Handle end of group if (!elei) { c2_error("Empty group."); } else if (next_expected) { c2_error("Missing rule before end of group."); } else if (elei > 1) { assert(2 == elei); assert(ops[1]); eles[0] = c2h_comb_tree(ops[1], eles[0], eles[1]); c2_ptr_reset(&eles[1]); } *presult = eles[0]; if (')' == pattern[offset]) ++offset; return offset; c2_parse_grp_fail: c2_freep(&eles[0]); c2_freep(&eles[1]); return -1; } #undef c2_error #define c2_error(format, ...) do { \ printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \ ## __VA_ARGS__); \ return -1; \ } while(0) /** * Parse the target part of a rule. */ static int c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) { // Initialize leaf presult->isbranch = false; presult->l = malloc(sizeof(c2_l_t)); if (!presult->l) c2_error("Failed to allocate memory for new leaf."); c2_l_t * const pleaf = presult->l; memcpy(pleaf, &leaf_def, sizeof(c2_l_t)); // Parse negation marks while ('!' == pattern[offset]) { pleaf->neg = !pleaf->neg; ++offset; C2H_SKIP_SPACES(); } // Copy target name out unsigned tgtlen = 0; for (; pattern[offset] && (isalnum(pattern[offset]) || '_' == pattern[offset]); ++offset) { ++tgtlen; } if (!tgtlen) c2_error("Empty target."); pleaf->tgt = mstrncpy(&pattern[offset - tgtlen], tgtlen); // Check for predefined targets for (unsigned i = 1; i < sizeof(C2_PREDEFS) / sizeof(C2_PREDEFS[0]); ++i) { if (!strcmp(C2_PREDEFS[i].name, pleaf->tgt)) { pleaf->predef = i; pleaf->type = C2_PREDEFS[i].type; pleaf->format = C2_PREDEFS[i].format; break; } } // Alias for predefined targets if (!pleaf->predef) { #define TGTFILL(pdefid) \ (pleaf->predef = pdefid, \ pleaf->type = C2_PREDEFS[pdefid].type, \ pleaf->format = C2_PREDEFS[pdefid].format) // if (!strcmp("WM_NAME", tgt) || !strcmp("_NET_WM_NAME", tgt)) // TGTFILL(C2_L_PNAME); #undef TGTFILL // Alias for custom properties #define TGTFILL(target, type, format) \ (pleaf->target = mstrcpy(target), \ pleaf->type = type, \ pleaf->format = format) // if (!strcmp("SOME_ALIAS")) // TGTFILL("ALIAS_TEXT", C2_L_TSTRING, 32); #undef TGTFILL } C2H_SKIP_SPACES(); // Parse target-on-frame flag if ('@' == pattern[offset]) { pleaf->tgt_onframe = true; ++offset; C2H_SKIP_SPACES(); } // Parse index if ('[' == pattern[offset]) { offset++; C2H_SKIP_SPACES(); int index = -1; char *endptr = NULL; index = strtol(pattern + offset, &endptr, 0); if (!endptr || pattern + offset == endptr) c2_error("No index number found after bracket."); if (index < 0) c2_error("Index number invalid."); if (pleaf->predef) c2_error("Predefined targets can't have index."); pleaf->index = index; offset = endptr - pattern; C2H_SKIP_SPACES(); if (']' != pattern[offset]) c2_error("Index end marker not found."); ++offset; C2H_SKIP_SPACES(); } // Parse target type and format if (':' == pattern[offset]) { ++offset; C2H_SKIP_SPACES(); // Look for format bool hasformat = false; int format = 0; { char *endptr = NULL; format = strtol(pattern + offset, &endptr, 0); assert(endptr); if ((hasformat = (endptr && endptr != pattern + offset))) offset = endptr - pattern; C2H_SKIP_SPACES(); } // Look for type enum c2_l_type type = C2_L_TUNDEFINED; { switch (pattern[offset]) { case 'w': type = C2_L_TWINDOW; break; case 'd': type = C2_L_TDRAWABLE; break; case 'c': type = C2_L_TCARDINAL; break; case 's': type = C2_L_TSTRING; break; case 'a': type = C2_L_TATOM; break; default: c2_error("Invalid type character."); } if (type) { if (pleaf->predef) { printf_errf("(): Warning: Type specified for a default target will be ignored."); } else { if (pleaf->type && type != pleaf->type) printf_errf("(): Warning: Default type overridden on target."); pleaf->type = type; } } offset++; C2H_SKIP_SPACES(); } // Default format if (!pleaf->format) { switch (pleaf->type) { case C2_L_TWINDOW: case C2_L_TDRAWABLE: case C2_L_TATOM: pleaf->format = 32; break; case C2_L_TSTRING: pleaf->format = 8; break; default: break; } } // Write format if (hasformat) { if (pleaf->predef) printf_errf("(): Warning: Format \"%d\" specified on a default target will be ignored.", format); else if (C2_L_TSTRING == pleaf->type) printf_errf("(): Warning: Format \"%d\" specified on a string target will be ignored.", format); else { if (pleaf->format && pleaf->format != format) printf_err("Warning: Default format %d overridden on target.", pleaf->format); pleaf->format = format; } } } if (!pleaf->type) c2_error("Target type cannot be determined."); // if (!pleaf->predef && !pleaf->format && C2_L_TSTRING != pleaf->type) // c2_error("Target format cannot be determined."); if (pleaf->format && 8 != pleaf->format && 16 != pleaf->format && 32 != pleaf->format) c2_error("Invalid format."); return offset; } /** * Parse the operator part of a leaf. */ static int c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult) { c2_l_t * const pleaf = presult->l; // Parse negation marks C2H_SKIP_SPACES(); while ('!' == pattern[offset]) { pleaf->neg = !pleaf->neg; ++offset; C2H_SKIP_SPACES(); } // Parse qualifiers if ('*' == pattern[offset] || '^' == pattern[offset] || '%' == pattern[offset] || '~' == pattern[offset]) { switch (pattern[offset]) { case '*': pleaf->match = C2_L_MCONTAINS; break; case '^': pleaf->match = C2_L_MSTART; break; case '%': pleaf->match = C2_L_MWILDCARD; break; case '~': pleaf->match = C2_L_MPCRE; break; default: assert(0); } ++offset; C2H_SKIP_SPACES(); } // Parse flags while ('?' == pattern[offset]) { pleaf->match_ignorecase = true; ++offset; C2H_SKIP_SPACES(); } // Parse operator while ('=' == pattern[offset] || '>' == pattern[offset] || '<' == pattern[offset]) { if ('=' == pattern[offset] && C2_L_OGT == pleaf->op) pleaf->op = C2_L_OGTEQ; else if ('=' == pattern[offset] && C2_L_OLT == pleaf->op) pleaf->op = C2_L_OLTEQ; else if (pleaf->op) { c2_error("Duplicate operator."); } else { switch (pattern[offset]) { case '=': pleaf->op = C2_L_OEQ; break; case '>': pleaf->op = C2_L_OGT; break; case '<': pleaf->op = C2_L_OLT; break; default: assert(0); } } ++offset; C2H_SKIP_SPACES(); } // Check for problems if (C2_L_OEQ != pleaf->op && (pleaf->match || pleaf->match_ignorecase)) c2_error("Exists/greater-than/less-than operators cannot have a qualifier."); return offset; } /** * Parse the pattern part of a leaf. */ static int c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) { c2_l_t * const pleaf = presult->l; // Exists operator cannot have pattern if (!pleaf->op) return offset; C2H_SKIP_SPACES(); char *endptr = NULL; // Check for boolean patterns if (!strcmp_wd("true", &pattern[offset])) { pleaf->ptntype = C2_L_PTINT; pleaf->ptnint = true; offset += strlen("true"); } else if (!strcmp_wd("false", &pattern[offset])) { pleaf->ptntype = C2_L_PTINT; pleaf->ptnint = false; offset += strlen("false"); } // Check for integer patterns else if (pleaf->ptnint = strtol(pattern + offset, &endptr, 0), pattern + offset != endptr) { pleaf->ptntype = C2_L_PTINT; offset = endptr - pattern; // Make sure we are stopping at the end of a word if (isalnum(pattern[offset])) c2_error("Trailing characters after a numeric pattern."); } // Check for string patterns else { bool raw = false; char delim = '\0'; // String flags if ('r' == tolower(pattern[offset])) { raw = true; ++offset; C2H_SKIP_SPACES(); } // Check for delimiters if ('\"' == pattern[offset] || '\'' == pattern[offset]) { pleaf->ptntype = C2_L_PTSTRING; delim = pattern[offset]; ++offset; } if (C2_L_PTSTRING != pleaf->ptntype) c2_error("Invalid pattern type."); // Parse the string now // We can't determine the length of the pattern, so we use the length // to the end of the pattern string -- currently escape sequences // cannot be converted to a string longer than itself. char *tptnstr = malloc((strlen(pattern + offset) + 1) * sizeof(char)); char *ptptnstr = tptnstr; pleaf->ptnstr = tptnstr; for (; pattern[offset] && delim != pattern[offset]; ++offset) { // Handle escape sequences if it's not a raw string if ('\\' == pattern[offset] && !raw) { switch(pattern[++offset]) { case '\\': *(ptptnstr++) = '\\'; break; case '\'': *(ptptnstr++) = '\''; break; case '\"': *(ptptnstr++) = '\"'; break; case 'a': *(ptptnstr++) = '\a'; break; case 'b': *(ptptnstr++) = '\b'; break; case 'f': *(ptptnstr++) = '\f'; break; case 'n': *(ptptnstr++) = '\n'; break; case 'r': *(ptptnstr++) = '\r'; break; case 't': *(ptptnstr++) = '\t'; break; case 'v': *(ptptnstr++) = '\v'; break; case 'o': case 'x': { char *tstr = mstrncpy(pattern + offset + 1, 2); char *pstr = NULL; long val = strtol(tstr, &pstr, ('o' == pattern[offset] ? 8: 16)); free(tstr); if (pstr != &tstr[2] || val <= 0) c2_error("Invalid octal/hex escape sequence."); assert(val < 256 && val >= 0); *(ptptnstr++) = val; offset += 2; break; } default: c2_error("Invalid escape sequence."); } } else { *(ptptnstr++) = pattern[offset]; } } if (!pattern[offset]) c2_error("Premature end of pattern string."); ++offset; *ptptnstr = '\0'; pleaf->ptnstr = mstrcpy(tptnstr); free(tptnstr); } C2H_SKIP_SPACES(); if (!pleaf->ptntype) c2_error("Invalid pattern type."); // Check if the type is correct if (!(((C2_L_TSTRING == pleaf->type || C2_L_TATOM == pleaf->type) && C2_L_PTSTRING == pleaf->ptntype) || ((C2_L_TCARDINAL == pleaf->type || C2_L_TWINDOW == pleaf->type || C2_L_TDRAWABLE == pleaf->type) && C2_L_PTINT == pleaf->ptntype))) c2_error("Pattern type incompatible with target type."); if (C2_L_PTINT == pleaf->ptntype && pleaf->match) c2_error("Integer/boolean pattern cannot have operator qualifiers."); if (C2_L_PTINT == pleaf->ptntype && pleaf->match_ignorecase) c2_error("Integer/boolean pattern cannot have flags."); if (C2_L_PTSTRING == pleaf->ptntype && (C2_L_OGT == pleaf->op || C2_L_OGTEQ == pleaf->op || C2_L_OLT == pleaf->op || C2_L_OLTEQ == pleaf->op)) c2_error("String pattern cannot have an arithmetic operator."); return offset; } /** * Parse a condition with legacy syntax. */ static int c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) { unsigned plen = strlen(pattern + offset); if (plen < 4 || ':' != pattern[offset + 1] || !strchr(pattern + offset + 2, ':')) c2_error("Legacy parser: Invalid format."); // Allocate memory for new leaf c2_l_t *pleaf = malloc(sizeof(c2_l_t)); if (!pleaf) printf_errfq(1, "(): Failed to allocate memory for new leaf."); presult->isbranch = false; presult->l = pleaf; memcpy(pleaf, &leaf_def, sizeof(c2_l_t)); pleaf->type = C2_L_TSTRING; pleaf->op = C2_L_OEQ; pleaf->ptntype = C2_L_PTSTRING; // Determine the pattern target #define TGTFILL(pdefid) \ (pleaf->predef = pdefid, \ pleaf->type = C2_PREDEFS[pdefid].type, \ pleaf->format = C2_PREDEFS[pdefid].format) switch (pattern[offset]) { case 'n': TGTFILL(C2_L_PNAME); break; case 'i': TGTFILL(C2_L_PCLASSI); break; case 'g': TGTFILL(C2_L_PCLASSG); break; case 'r': TGTFILL(C2_L_PROLE); break; default: c2_error("Target \"%c\" invalid.\n", pattern[offset]); } #undef TGTFILL offset += 2; // Determine the match type switch (pattern[offset]) { case 'e': pleaf->match = C2_L_MEXACT; break; case 'a': pleaf->match = C2_L_MCONTAINS; break; case 's': pleaf->match = C2_L_MSTART; break; case 'w': pleaf->match = C2_L_MWILDCARD; break; case 'p': pleaf->match = C2_L_MPCRE; break; default: c2_error("Type \"%c\" invalid.\n", pattern[offset]); } ++offset; // Determine the pattern flags while (':' != pattern[offset]) { switch (pattern[offset]) { case 'i': pleaf->match_ignorecase = true; break; default: c2_error("Flag \"%c\" invalid.", pattern[offset]); } ++offset; } ++offset; // Copy the pattern pleaf->ptnstr = mstrcpy(pattern + offset); if (!c2_l_postprocess(ps, pleaf)) return -1; return offset; } #undef c2_error #define c2_error(format, ...) { \ printf_err(format, ## __VA_ARGS__); \ return false; } /** * Do postprocessing on a condition leaf. */ static bool c2_l_postprocess(session_t *ps, c2_l_t *pleaf) { // Give a pattern type to a leaf with exists operator, if needed if (C2_L_OEXISTS == pleaf->op && !pleaf->ptntype) { pleaf->ptntype = (C2_L_TSTRING == pleaf->type ? C2_L_PTSTRING: C2_L_PTINT); } // Get target atom if it's not a predefined one if (!pleaf->predef) { pleaf->tgtatom = get_atom(ps, pleaf->tgt); if (!pleaf->tgtatom) c2_error("Failed to get atom for target \"%s\".", pleaf->tgt); } // Insert target Atom into atom track list if (pleaf->tgtatom) { bool found = false; for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) { if (pleaf->tgtatom == platom->atom) { found = true; break; } } if (!found) { latom_t *pnew = malloc(sizeof(latom_t)); if (!pnew) printf_errfq(1, "(): Failed to allocate memory for new track atom."); pnew->next = ps->track_atom_lst; pnew->atom = pleaf->tgtatom; ps->track_atom_lst = pnew; } } // Enable specific tracking options in compton if needed by the condition // TODO: Add track_leader if (pleaf->predef) { switch (pleaf->predef) { case C2_L_PFOCUSED: ps->o.track_focus = true; break; // case C2_L_PROUNDED: ps->o.detect_rounded_corners = true; break; case C2_L_PNAME: case C2_L_PCLASSG: case C2_L_PCLASSI: case C2_L_PROLE: ps->o.track_wdata = true; break; default: break; } } // Warn about lower case characters in target name if (!pleaf->predef) { for (const char *pc = pleaf->tgt; *pc; ++pc) { if (islower(*pc)) { printf_errf("(): Warning: Lowercase character in target name \"%s\".", pleaf->tgt); break; } } } // PCRE patterns if (C2_L_PTSTRING == pleaf->ptntype && C2_L_MPCRE == pleaf->match) { #ifdef CONFIG_REGEX_PCRE const char *error = NULL; int erroffset = 0; int options = 0; // Ignore case flag if (pleaf->match_ignorecase) options |= PCRE_CASELESS; // Compile PCRE expression pleaf->regex_pcre = pcre_compile(pleaf->ptnstr, options, &error, &erroffset, NULL); if (!pleaf->regex_pcre) c2_error("Pattern \"%s\": PCRE regular expression parsing failed on " "offset %d: %s", pleaf->ptnstr, erroffset, error); #ifdef CONFIG_REGEX_PCRE_JIT pleaf->regex_pcre_extra = pcre_study(pleaf->regex_pcre, PCRE_STUDY_JIT_COMPILE, &error); if (!pleaf->regex_pcre_extra) { printf("Pattern \"%s\": PCRE regular expression study failed: %s", pleaf->ptnstr, error); } #endif // Free the target string // free(pleaf->tgt); // pleaf->tgt = NULL; #else c2_error("PCRE regular expression support not compiled in."); #endif } return true; } /** * Free a condition tree. */ static void c2_free(c2_ptr_t p) { // For a branch element if (p.isbranch) { c2_b_t * const pbranch = p.b; if (!pbranch) return; c2_free(pbranch->opr1); c2_free(pbranch->opr2); free(pbranch); } // For a leaf element else { c2_l_t * const pleaf = p.l; if (!pleaf) return; free(pleaf->tgt); free(pleaf->ptnstr); #ifdef CONFIG_REGEX_PCRE pcre_free(pleaf->regex_pcre); LPCRE_FREE_STUDY(pleaf->regex_pcre_extra); #endif free(pleaf); } } /** * Free a condition tree in c2_lptr_t. */ c2_lptr_t * c2_free_lptr(c2_lptr_t *lp) { if (!lp) return NULL; c2_lptr_t *pnext = lp->next; c2_free(lp->ptr); free(lp); return pnext; } /** * Get a string representation of a rule target. */ static const char * c2h_dump_str_tgt(const c2_l_t *pleaf) { if (pleaf->predef) return C2_PREDEFS[pleaf->predef].name; else return pleaf->tgt; } /** * Get a string representation of a target. */ static const char * c2h_dump_str_type(const c2_l_t *pleaf) { switch (pleaf->type) { case C2_L_TWINDOW: return "w"; case C2_L_TDRAWABLE: return "d"; case C2_L_TCARDINAL: return "c"; case C2_L_TSTRING: return "s"; case C2_L_TATOM: return "a"; case C2_L_TUNDEFINED: break; } return NULL; } /** * Dump a condition tree. */ static void c2_dump_raw(c2_ptr_t p) { // For a branch if (p.isbranch) { const c2_b_t * const pbranch = p.b; if (!pbranch) return; if (pbranch->neg) putchar('!'); printf("("); c2_dump_raw(pbranch->opr1); switch (pbranch->op) { case C2_B_OAND: printf(" && "); break; case C2_B_OOR: printf(" || "); break; case C2_B_OXOR: printf(" XOR "); break; default: assert(0); break; } c2_dump_raw(pbranch->opr2); printf(")"); } // For a leaf else { const c2_l_t * const pleaf = p.l; if (!pleaf) return; if (C2_L_OEXISTS == pleaf->op && pleaf->neg) putchar('!'); // Print target name, type, and format { printf("%s", c2h_dump_str_tgt(pleaf)); if (pleaf->tgt_onframe) putchar('@'); if (pleaf->index >= 0) printf("[%d]", pleaf->index); printf(":%d%s", pleaf->format, c2h_dump_str_type(pleaf)); } // Print operator putchar(' '); if (C2_L_OEXISTS != pleaf->op && pleaf->neg) putchar('!'); switch (pleaf->match) { case C2_L_MEXACT: break; case C2_L_MCONTAINS: putchar('*'); break; case C2_L_MSTART: putchar('^'); break; case C2_L_MPCRE: putchar('~'); break; case C2_L_MWILDCARD: putchar('%'); break; } if (pleaf->match_ignorecase) putchar('?'); switch (pleaf->op) { case C2_L_OEXISTS: break; case C2_L_OEQ: fputs("=", stdout); break; case C2_L_OGT: fputs(">", stdout); break; case C2_L_OGTEQ: fputs(">=", stdout); break; case C2_L_OLT: fputs("<", stdout); break; case C2_L_OLTEQ: fputs("<=", stdout); break; } if (C2_L_OEXISTS == pleaf->op) return; // Print pattern putchar(' '); switch (pleaf->ptntype) { case C2_L_PTINT: printf("%ld", pleaf->ptnint); break; case C2_L_PTSTRING: // TODO: Escape string before printing out? printf("\"%s\"", pleaf->ptnstr); break; default: assert(0); break; } } } /** * Get the type atom of a condition. */ static Atom c2_get_atom_type(const c2_l_t *pleaf) { switch (pleaf->type) { case C2_L_TCARDINAL: return XA_CARDINAL; case C2_L_TWINDOW: return XA_WINDOW; case C2_L_TSTRING: return XA_STRING; case C2_L_TATOM: return XA_ATOM; case C2_L_TDRAWABLE: return XA_DRAWABLE; default: assert(0); break; } assert(0); return AnyPropertyType; } /** * Match a window against a single leaf window condition. * * For internal use. */ static inline void c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf, bool *pres, bool *perr) { assert(pleaf); const Window wid = (pleaf->tgt_onframe ? w->client_win: w->id); // Return if wid is missing if (!pleaf->predef && !wid) return; const int idx = (pleaf->index < 0 ? 0: pleaf->index); switch (pleaf->ptntype) { // Deal with integer patterns case C2_L_PTINT: { long tgt = 0; // Get the value // A predefined target if (pleaf->predef) { *perr = false; switch (pleaf->predef) { case C2_L_PID: tgt = wid; break; case C2_L_PX: tgt = w->a.x; break; case C2_L_PY: tgt = w->a.y; break; case C2_L_PX2: tgt = w->a.x + w->widthb; break; case C2_L_PY2: tgt = w->a.y + w->heightb; break; case C2_L_PWIDTH: tgt = w->a.width; break; case C2_L_PHEIGHT: tgt = w->a.height; break; case C2_L_PWIDTHB: tgt = w->widthb; break; case C2_L_PHEIGHTB: tgt = w->heightb; break; case C2_L_PBDW: tgt = w->a.border_width; break; case C2_L_PFULLSCREEN: tgt = win_is_fullscreen(ps, w); break; case C2_L_POVREDIR: tgt = w->a.override_redirect; break; case C2_L_PARGB: tgt = (WMODE_ARGB == w->mode); break; case C2_L_PFOCUSED: tgt = win_is_focused_real(ps, w); break; case C2_L_PWMWIN: tgt = w->wmwin; break; case C2_L_PBSHAPED: tgt = w->bounding_shaped; break; case C2_L_PROUNDED: tgt = w->rounded_corners; break; case C2_L_PCLIENT: tgt = w->client_win; break; case C2_L_PLEADER: tgt = w->leader; break; default: *perr = true; assert(0); break; } } // A raw window property else { winprop_t prop = wid_get_prop_adv(ps, wid, pleaf->tgtatom, idx, 1L, c2_get_atom_type(pleaf), pleaf->format); if (prop.nitems) { *perr = false; tgt = winprop_get_int(prop); } free_winprop(&prop); } if (*perr) return; // Do comparison switch (pleaf->op) { case C2_L_OEXISTS: *pres = (pleaf->predef ? tgt: true); break; case C2_L_OEQ: *pres = (tgt == pleaf->ptnint); break; case C2_L_OGT: *pres = (tgt > pleaf->ptnint); break; case C2_L_OGTEQ: *pres = (tgt >= pleaf->ptnint); break; case C2_L_OLT: *pres = (tgt < pleaf->ptnint); break; case C2_L_OLTEQ: *pres = (tgt <= pleaf->ptnint); break; default: *perr = true; assert(0); break; } } break; // String patterns case C2_L_PTSTRING: { const char *tgt = NULL; char *tgt_free = NULL; // A predefined target if (pleaf->predef) { switch (pleaf->predef) { case C2_L_PWINDOWTYPE: tgt = WINTYPES[w->window_type]; break; case C2_L_PNAME: tgt = w->name; break; case C2_L_PCLASSG: tgt = w->class_general; break; case C2_L_PCLASSI: tgt = w->class_instance; break; case C2_L_PROLE: tgt = w->role; break; default: assert(0); break; } } // If it's an atom type property, convert atom to string else if (C2_L_TATOM == pleaf->type) { winprop_t prop = wid_get_prop_adv(ps, wid, pleaf->tgtatom, idx, 1L, c2_get_atom_type(pleaf), pleaf->format); Atom atom = winprop_get_int(prop); if (atom) { tgt_free = XGetAtomName(ps->dpy, atom); } if (tgt_free) { tgt = tgt_free; } free_winprop(&prop); } // Otherwise, just fetch the string list else { char **strlst = NULL; int nstr; if (wid_get_text_prop(ps, wid, pleaf->tgtatom, &strlst, &nstr) && nstr > idx) { tgt_free = mstrcpy(strlst[idx]); tgt = tgt_free; } if (strlst) XFreeStringList(strlst); } if (tgt) { *perr = false; } else { return; } // Actual matching switch (pleaf->op) { case C2_L_OEXISTS: *pres = true; break; case C2_L_OEQ: switch (pleaf->match) { case C2_L_MEXACT: if (pleaf->match_ignorecase) *pres = !strcasecmp(tgt, pleaf->ptnstr); else *pres = !strcmp(tgt, pleaf->ptnstr); break; case C2_L_MCONTAINS: if (pleaf->match_ignorecase) *pres = strcasestr(tgt, pleaf->ptnstr); else *pres = strstr(tgt, pleaf->ptnstr); break; case C2_L_MSTART: if (pleaf->match_ignorecase) *pres = !strncasecmp(tgt, pleaf->ptnstr, strlen(pleaf->ptnstr)); else *pres = !strncmp(tgt, pleaf->ptnstr, strlen(pleaf->ptnstr)); break; case C2_L_MWILDCARD: { int flags = 0; if (pleaf->match_ignorecase) flags |= FNM_CASEFOLD; *pres = !fnmatch(pleaf->ptnstr, tgt, flags); } break; case C2_L_MPCRE: #ifdef CONFIG_REGEX_PCRE *pres = (pcre_exec(pleaf->regex_pcre, pleaf->regex_pcre_extra, tgt, strlen(tgt), 0, 0, NULL, 0) >= 0); #else assert(0); #endif break; } break; default: *perr = true; assert(0); } // Free the string after usage, if necessary if (tgt_free) { if (C2_L_TATOM == pleaf->type) cxfree(tgt_free); else free(tgt_free); } } break; default: assert(0); break; } } /** * Match a window against a single window condition. * * @return true if matched, false otherwise. */ static bool c2_match_once(session_t *ps, win *w, const c2_ptr_t cond) { bool result = false; bool error = true; // Handle a branch if (cond.isbranch) { const c2_b_t *pb = cond.b; if (!pb) return false; error = false; switch (pb->op) { case C2_B_OAND: result = (c2_match_once(ps, w, pb->opr1) && c2_match_once(ps, w, pb->opr2)); break; case C2_B_OOR: result = (c2_match_once(ps, w, pb->opr1) || c2_match_once(ps, w, pb->opr2)); break; case C2_B_OXOR: result = (c2_match_once(ps, w, pb->opr1) != c2_match_once(ps, w, pb->opr2)); break; default: error = true; assert(0); } #ifdef DEBUG_WINMATCH printf_dbgf("(%#010lx): branch: result = %d, pattern = ", w->id, result); c2_dump(cond); #endif } // Handle a leaf else { const c2_l_t *pleaf = cond.l; if (!pleaf) return false; c2_match_once_leaf(ps, w, pleaf, &result, &error); // For EXISTS operator, no errors are fatal if (C2_L_OEXISTS == pleaf->op && error) { result = false; error = false; } #ifdef DEBUG_WINMATCH printf_dbgf("(%#010lx): leaf: result = %d, error = %d, " "client = %#010lx, pattern = ", w->id, result, error, w->client_win); c2_dump(cond); #endif } // Postprocess the result if (error) result = false; if (cond.isbranch ? cond.b->neg: cond.l->neg) result = !result; return result; } /** * Match a window against a condition linked list. * * @param cache a place to cache the last matched condition * @param pdata a place to return the data * @return true if matched, false otherwise. */ bool c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst, const c2_lptr_t **cache, void **pdata) { assert(IsViewable == w->a.map_state); // Check if the cached entry matches firstly if (cache && *cache && c2_match_once(ps, w, (*cache)->ptr)) { if (pdata) *pdata = (*cache)->data; return true; } // Then go through the whole linked list for (; condlst; condlst = condlst->next) { if (c2_match_once(ps, w, condlst->ptr)) { if (cache) *cache = condlst; if (pdata) *pdata = condlst->data; return true; } } return false; } compton-0.1~beta2+20150922/src/c2.h000066400000000000000000000171221260012102400163000ustar00rootroot00000000000000/* * Compton - a compositor for X11 * * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * * Copyright (c) 2011-2013, Christopher Jeffrey * See LICENSE for more information. * */ #include "common.h" #include #include // libpcre #ifdef CONFIG_REGEX_PCRE #include // For compatiblity with opr1 = p1; p.b->opr2 = p2; p.b->op = op; return p; } /** * Get the precedence value of a condition branch operator. */ static inline int c2h_b_opp(c2_b_op_t op) { switch (op) { case C2_B_OAND: return 2; case C2_B_OOR: return 1; case C2_B_OXOR: return 1; default: break; } assert(0); return 0; } /** * Compare precedence of two condition branch operators. * * Associativity is left-to-right, forever. * * @return positive number if op1 > op2, 0 if op1 == op2 in precedence, * negative number otherwise */ static inline int c2h_b_opcmp(c2_b_op_t op1, c2_b_op_t op2) { return c2h_b_opp(op1) - c2h_b_opp(op2); } static int c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level); static int c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult); static int c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult); static int c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult); static int c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult); static bool c2_l_postprocess(session_t *ps, c2_l_t *pleaf); static void c2_free(c2_ptr_t p); /** * Wrapper of c2_free(). */ static inline void c2_freep(c2_ptr_t *pp) { if (pp) { c2_free(*pp); c2_ptr_reset(pp); } } static const char * c2h_dump_str_tgt(const c2_l_t *pleaf); static const char * c2h_dump_str_type(const c2_l_t *pleaf); static void c2_dump_raw(c2_ptr_t p); /** * Wrapper of c2_dump_raw(). */ static inline void c2_dump(c2_ptr_t p) { c2_dump_raw(p); printf("\n"); fflush(stdout); } static Atom c2_get_atom_type(const c2_l_t *pleaf); static bool c2_match_once(session_t *ps, win *w, const c2_ptr_t cond); compton-0.1~beta2+20150922/src/common.h000066400000000000000000002011601260012102400172610ustar00rootroot00000000000000/* * Compton - a compositor for X11 * * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * * Copyright (c) 2011-2013, Christopher Jeffrey * See LICENSE for more information. * */ #ifndef COMPTON_COMMON_H #define COMPTON_COMMON_H // === Options === // Debug options, enable them using -D in CFLAGS // #define DEBUG_BACKTRACE 1 // #define DEBUG_REPAINT 1 // #define DEBUG_EVENTS 1 // #define DEBUG_RESTACK 1 // #define DEBUG_WINTYPE 1 // #define DEBUG_CLIENTWIN 1 // #define DEBUG_WINDATA 1 // #define DEBUG_WINMATCH 1 // #define DEBUG_REDIR 1 // #define DEBUG_ALLOC_REG 1 // #define DEBUG_FRAME 1 // #define DEBUG_LEADER 1 // #define DEBUG_C2 1 // #define DEBUG_GLX 1 // #define DEBUG_GLX_GLSL 1 // #define DEBUG_GLX_ERR 1 // #define DEBUG_GLX_MARK 1 // #define DEBUG_GLX_PAINTREG 1 // #define MONITOR_REPAINT 1 // Whether to enable PCRE regular expression support in blacklists, enabled // by default // #define CONFIG_REGEX_PCRE 1 // Whether to enable JIT support of libpcre. This may cause problems on PaX // kernels. // #define CONFIG_REGEX_PCRE_JIT 1 // Whether to enable parsing of configuration files using libconfig. // #define CONFIG_LIBCONFIG 1 // Whether we are using a legacy version of libconfig (1.3.x). // #define CONFIG_LIBCONFIG_LEGACY 1 // Whether to enable DRM VSync support // #define CONFIG_VSYNC_DRM 1 // Whether to enable OpenGL support // #define CONFIG_VSYNC_OPENGL 1 // Whether to enable GLX GLSL support // #define CONFIG_VSYNC_OPENGL_GLSL 1 // Whether to enable GLX FBO support // #define CONFIG_VSYNC_OPENGL_FBO 1 // Whether to enable DBus support with libdbus. // #define CONFIG_DBUS 1 // Whether to enable condition support. // #define CONFIG_C2 1 // Whether to enable X Sync support. // #define CONFIG_XSYNC 1 // Whether to enable GLX Sync support. // #define CONFIG_GLX_XSYNC 1 #if !defined(CONFIG_C2) && defined(DEBUG_C2) #error Cannot enable c2 debugging without c2 support. #endif #if (!defined(CONFIG_XSYNC) || !defined(CONFIG_VSYNC_OPENGL)) && defined(CONFIG_GLX_SYNC) #error Cannot enable GL sync without X Sync / OpenGL support. #endif #ifndef COMPTON_VERSION #define COMPTON_VERSION "unknown" #endif #if defined(DEBUG_ALLOC_REG) #define DEBUG_BACKTRACE 1 #endif // === Includes === // For some special functions #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_XSYNC #include #endif #ifdef CONFIG_XINERAMA #include #endif // Workarounds for missing definitions in very old versions of X headers, // thanks to consolers for reporting #ifndef PictOpDifference #define PictOpDifference 0x39 #endif // libconfig #ifdef CONFIG_LIBCONFIG #include #include #endif // libdbus #ifdef CONFIG_DBUS #include #endif #ifdef CONFIG_VSYNC_OPENGL // libGL #if defined(CONFIG_VSYNC_OPENGL_GLSL) || defined(CONFIG_VSYNC_OPENGL_FBO) #define GL_GLEXT_PROTOTYPES #endif #include // Workarounds for missing definitions in some broken GL drivers, thanks to // douglasp and consolers for reporting #ifndef GL_TEXTURE_RECTANGLE #define GL_TEXTURE_RECTANGLE 0x84F5 #endif #ifndef GLX_BACK_BUFFER_AGE_EXT #define GLX_BACK_BUFFER_AGE_EXT 0x20F4 #endif #endif // === Macros === #define MSTR_(s) #s #define MSTR(s) MSTR_(s) /// @brief Wrapper for gcc branch prediction builtin, for likely branch. #define likely(x) __builtin_expect(!!(x), 1) /// @brief Wrapper for gcc branch prediction builtin, for unlikely branch. #define unlikely(x) __builtin_expect(!!(x), 0) /// Print out an error message. #define printf_err(format, ...) \ fprintf(stderr, format "\n", ## __VA_ARGS__) /// Print out an error message with function name. #define printf_errf(format, ...) \ printf_err("%s" format, __func__, ## __VA_ARGS__) /// Print out an error message with function name, and quit with a /// specific exit code. #define printf_errfq(code, format, ...) { \ printf_err("%s" format, __func__, ## __VA_ARGS__); \ exit(code); \ } /// Print out a debug message. #define printf_dbg(format, ...) \ printf(format, ## __VA_ARGS__); \ fflush(stdout) /// Print out a debug message with function name. #define printf_dbgf(format, ...) \ printf_dbg("%s" format, __func__, ## __VA_ARGS__) // Use #s here to prevent macro expansion /// Macro used for shortening some debugging code. #define CASESTRRET(s) case s: return #s // X resource checker #ifdef DEBUG_XRC #include "xrescheck.h" #endif // === Constants === #if !(COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2) #error libXcomposite version unsupported #endif /// @brief Length of generic buffers. #define BUF_LEN 80 #define ROUNDED_PERCENT 0.05 #define ROUNDED_PIXELS 10 #define OPAQUE 0xffffffff #define REGISTER_PROP "_NET_WM_CM_S" #define TIME_MS_MAX LONG_MAX #define FADE_DELTA_TOLERANCE 0.2 #define SWOPTI_TOLERANCE 3000 #define TIMEOUT_RUN_TOLERANCE 0.05 #define WIN_GET_LEADER_MAX_RECURSION 20 #define SEC_WRAP (15L * 24L * 60L * 60L) #define NS_PER_SEC 1000000000L #define US_PER_SEC 1000000L #define MS_PER_SEC 1000 #define XRFILTER_CONVOLUTION "convolution" #define XRFILTER_GAUSSIAN "gaussian" #define XRFILTER_BINOMIAL "binomial" /// @brief Maximum OpenGL FBConfig depth. #define OPENGL_MAX_DEPTH 32 /// @brief Maximum OpenGL buffer age. #define CGLX_MAX_BUFFER_AGE 5 /// @brief Maximum passes for blur. #define MAX_BLUR_PASS 5 // Window flags // Window size is changed #define WFLAG_SIZE_CHANGE 0x0001 // Window size/position is changed #define WFLAG_POS_CHANGE 0x0002 // Window opacity / dim state changed #define WFLAG_OPCT_CHANGE 0x0004 // === Types === typedef uint32_t opacity_t; typedef long time_ms_t; typedef enum { WINTYPE_UNKNOWN, WINTYPE_DESKTOP, WINTYPE_DOCK, WINTYPE_TOOLBAR, WINTYPE_MENU, WINTYPE_UTILITY, WINTYPE_SPLASH, WINTYPE_DIALOG, WINTYPE_NORMAL, WINTYPE_DROPDOWN_MENU, WINTYPE_POPUP_MENU, WINTYPE_TOOLTIP, WINTYPE_NOTIFY, WINTYPE_COMBO, WINTYPE_DND, NUM_WINTYPES } wintype_t; /// Enumeration type to represent switches. typedef enum { OFF, // false ON, // true UNSET } switch_t; /// Structure representing a X geometry. typedef struct { int wid; int hei; int x; int y; } geometry_t; /// A structure representing margins around a rectangle. typedef struct { int top; int left; int bottom; int right; } margin_t; // Or use cmemzero(). #define MARGIN_INIT { 0, 0, 0, 0 } /// Enumeration type of window painting mode. typedef enum { WMODE_TRANS, WMODE_SOLID, WMODE_ARGB } winmode_t; /// Structure representing needed window updates. typedef struct { bool shadow : 1; bool fade : 1; bool focus : 1; bool invert_color : 1; } win_upd_t; /// Structure representing Window property value. typedef struct { // All pointers have the same length, right? // I wanted to use anonymous union but it's a GNU extension... union { unsigned char *p8; short *p16; long *p32; } data; unsigned long nitems; Atom type; int format; } winprop_t; typedef struct _ignore { struct _ignore *next; unsigned long sequence; } ignore_t; enum wincond_target { CONDTGT_NAME, CONDTGT_CLASSI, CONDTGT_CLASSG, CONDTGT_ROLE, }; enum wincond_type { CONDTP_EXACT, CONDTP_ANYWHERE, CONDTP_FROMSTART, CONDTP_WILDCARD, CONDTP_REGEX_PCRE, }; #define CONDF_IGNORECASE 0x0001 /// VSync modes. typedef enum { VSYNC_NONE, VSYNC_DRM, VSYNC_OPENGL, VSYNC_OPENGL_OML, VSYNC_OPENGL_SWC, VSYNC_OPENGL_MSWC, NUM_VSYNC, } vsync_t; /// @brief Possible backends of compton. enum backend { BKEND_XRENDER, BKEND_GLX, BKEND_XR_GLX_HYBRID, NUM_BKEND, }; /// @brief Possible swap methods. enum { SWAPM_BUFFER_AGE = -1, SWAPM_UNDEFINED = 0, SWAPM_COPY = 1, SWAPM_EXCHANGE = 2, }; typedef struct _glx_texture glx_texture_t; #ifdef CONFIG_VSYNC_OPENGL #ifdef DEBUG_GLX_DEBUG_CONTEXT typedef GLXContext (*f_glXCreateContextAttribsARB) (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); typedef void (*GLDEBUGPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam); typedef void (*f_DebugMessageCallback) (GLDEBUGPROC, void *userParam); #endif typedef int (*f_WaitVideoSync) (int, int, unsigned *); typedef int (*f_GetVideoSync) (unsigned *); typedef Bool (*f_GetSyncValuesOML) (Display* dpy, GLXDrawable drawable, int64_t* ust, int64_t* msc, int64_t* sbc); typedef Bool (*f_WaitForMscOML) (Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t* ust, int64_t* msc, int64_t* sbc); typedef int (*f_SwapIntervalSGI) (int interval); typedef int (*f_SwapIntervalMESA) (unsigned int interval); typedef void (*f_BindTexImageEXT) (Display *display, GLXDrawable drawable, int buffer, const int *attrib_list); typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, int buffer); typedef void (*f_CopySubBuffer) (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); #ifdef CONFIG_GLX_SYNC // Looks like duplicate typedef of the same type is safe? typedef int64_t GLint64; typedef uint64_t GLuint64; typedef struct __GLsync *GLsync; #ifndef GL_SYNC_FLUSH_COMMANDS_BIT #define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 #endif #ifndef GL_TIMEOUT_IGNORED #define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull #endif #ifndef GL_ALREADY_SIGNALED #define GL_ALREADY_SIGNALED 0x911A #endif #ifndef GL_TIMEOUT_EXPIRED #define GL_TIMEOUT_EXPIRED 0x911B #endif #ifndef GL_CONDITION_SATISFIED #define GL_CONDITION_SATISFIED 0x911C #endif #ifndef GL_WAIT_FAILED #define GL_WAIT_FAILED 0x911D #endif typedef GLsync (*f_FenceSync) (GLenum condition, GLbitfield flags); typedef GLboolean (*f_IsSync) (GLsync sync); typedef void (*f_DeleteSync) (GLsync sync); typedef GLenum (*f_ClientWaitSync) (GLsync sync, GLbitfield flags, GLuint64 timeout); typedef void (*f_WaitSync) (GLsync sync, GLbitfield flags, GLuint64 timeout); typedef GLsync (*f_ImportSyncEXT) (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); #endif #ifdef DEBUG_GLX_MARK typedef void (*f_StringMarkerGREMEDY) (GLsizei len, const void *string); typedef void (*f_FrameTerminatorGREMEDY) (void); #endif /// @brief Wrapper of a GLX FBConfig. typedef struct { GLXFBConfig cfg; GLint texture_fmt; GLint texture_tgts; bool y_inverted; } glx_fbconfig_t; /// @brief Wrapper of a binded GLX texture. struct _glx_texture { GLuint texture; GLXPixmap glpixmap; Pixmap pixmap; GLenum target; unsigned width; unsigned height; unsigned depth; bool y_inverted; }; #ifdef CONFIG_VSYNC_OPENGL_GLSL typedef struct { /// Fragment shader for blur. GLuint frag_shader; /// GLSL program for blur. GLuint prog; /// Location of uniform "offset_x" in blur GLSL program. GLint unifm_offset_x; /// Location of uniform "offset_y" in blur GLSL program. GLint unifm_offset_y; /// Location of uniform "factor_center" in blur GLSL program. GLint unifm_factor_center; } glx_blur_pass_t; typedef struct { /// Framebuffer used for blurring. GLuint fbo; /// Textures used for blurring. GLuint textures[2]; /// Width of the textures. int width; /// Height of the textures. int height; } glx_blur_cache_t; typedef struct { /// GLSL program. GLuint prog; /// Location of uniform "opacity" in window GLSL program. GLint unifm_opacity; /// Location of uniform "invert_color" in blur GLSL program. GLint unifm_invert_color; /// Location of uniform "tex" in window GLSL program. GLint unifm_tex; } glx_prog_main_t; #define GLX_PROG_MAIN_INIT { \ .prog = 0, \ .unifm_opacity = -1, \ .unifm_invert_color = -1, \ .unifm_tex = -1, \ } #endif #endif typedef struct { Pixmap pixmap; Picture pict; glx_texture_t *ptex; } paint_t; #define PAINT_INIT { .pixmap = None, .pict = None } typedef struct { int size; double *data; } conv; /// Linked list type of atoms. typedef struct _latom { Atom atom; struct _latom *next; } latom_t; /// A representation of raw region data typedef struct { XRectangle *rects; int nrects; } reg_data_t; #define REG_DATA_INIT { NULL, 0 } struct _timeout_t; struct _win; typedef struct _c2_lptr c2_lptr_t; /// Structure representing all options. typedef struct _options_t { // === General === /// The configuration file we used. char *config_file; /// Path to write PID to. char *write_pid_path; /// The display name we used. NULL means we are using the value of the /// DISPLAY environment variable. char *display; /// Safe representation of display name. char *display_repr; /// The backend in use. enum backend backend; /// Whether to sync X drawing to avoid certain delay issues with /// GLX backend. bool xrender_sync; /// Whether to sync X drawing with X Sync fence. bool xrender_sync_fence; /// Whether to avoid using stencil buffer under GLX backend. Might be /// unsafe. bool glx_no_stencil; /// Whether to copy unmodified regions from front buffer. bool glx_copy_from_front; /// Whether to use glXCopySubBufferMESA() to update screen. bool glx_use_copysubbuffermesa; /// Whether to avoid rebinding pixmap on window damage. bool glx_no_rebind_pixmap; /// GLX swap method we assume OpenGL uses. int glx_swap_method; /// Whether to use GL_EXT_gpu_shader4 to (hopefully) accelerates blurring. bool glx_use_gpushader4; /// Custom fragment shader for painting windows, as a string. char *glx_fshader_win_str; #ifdef CONFIG_VSYNC_OPENGL_GLSL /// Custom GLX program used for painting window. glx_prog_main_t glx_prog_win; #endif /// Whether to fork to background. bool fork_after_register; /// Whether to detect rounded corners. bool detect_rounded_corners; /// Whether to paint on X Composite overlay window instead of root /// window. bool paint_on_overlay; /// Force painting of window content with blending. bool force_win_blend; /// Resize damage for a specific number of pixels. int resize_damage; /// Whether to unredirect all windows if a full-screen opaque window /// is detected. bool unredir_if_possible; /// List of conditions of windows to ignore as a full-screen window /// when determining if a window could be unredirected. c2_lptr_t *unredir_if_possible_blacklist; /// Delay before unredirecting screen. time_ms_t unredir_if_possible_delay; /// Forced redirection setting through D-Bus. switch_t redirected_force; /// Whether to stop painting. Controlled through D-Bus. switch_t stoppaint_force; /// Whether to re-redirect screen on root size change. bool reredir_on_root_change; /// Whether to reinitialize GLX on root size change. bool glx_reinit_on_root_change; /// Whether to enable D-Bus support. bool dbus; /// Path to log file. char *logpath; /// Number of cycles to paint in benchmark mode. 0 for disabled. int benchmark; /// Window to constantly repaint in benchmark mode. 0 for full-screen. Window benchmark_wid; /// A list of conditions of windows not to paint. c2_lptr_t *paint_blacklist; /// Whether to avoid using XCompositeNameWindowPixmap(), for debugging. bool no_name_pixmap; /// Whether to work under synchronized mode for debugging. bool synchronize; /// Whether to show all X errors. bool show_all_xerrors; /// Whether to avoid acquiring X Selection. bool no_x_selection; // === VSync & software optimization === /// User-specified refresh rate. int refresh_rate; /// Whether to enable refresh-rate-based software optimization. bool sw_opti; /// VSync method to use; vsync_t vsync; /// Whether to enable double buffer. bool dbe; /// Whether to do VSync aggressively. bool vsync_aggressive; /// Whether to use glFinish() instead of glFlush() for (possibly) better /// VSync yet probably higher CPU usage. bool vsync_use_glfinish; // === Shadow === /// Enable/disable shadow for specific window types. bool wintype_shadow[NUM_WINTYPES]; /// Red, green and blue tone of the shadow. double shadow_red, shadow_green, shadow_blue; int shadow_radius; int shadow_offset_x, shadow_offset_y; double shadow_opacity; bool clear_shadow; /// Geometry of a region in which shadow is not painted on. geometry_t shadow_exclude_reg_geom; /// Shadow blacklist. A linked list of conditions. c2_lptr_t *shadow_blacklist; /// Whether bounding-shaped window should be ignored. bool shadow_ignore_shaped; /// Whether to respect _COMPTON_SHADOW. bool respect_prop_shadow; /// Whether to crop shadow to the very Xinerama screen. bool xinerama_shadow_crop; // === Fading === /// Enable/disable fading for specific window types. bool wintype_fade[NUM_WINTYPES]; /// How much to fade in in a single fading step. opacity_t fade_in_step; /// How much to fade out in a single fading step. opacity_t fade_out_step; /// Fading time delta. In milliseconds. time_ms_t fade_delta; /// Whether to disable fading on window open/close. bool no_fading_openclose; /// Whether to disable fading on ARGB managed destroyed windows. bool no_fading_destroyed_argb; /// Fading blacklist. A linked list of conditions. c2_lptr_t *fade_blacklist; // === Opacity === /// Default opacity for specific window types double wintype_opacity[NUM_WINTYPES]; /// Default opacity for inactive windows. /// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for /// not enabled, default. opacity_t inactive_opacity; /// Default opacity for inactive windows. opacity_t active_opacity; /// Whether inactive_opacity overrides the opacity set by window /// attributes. bool inactive_opacity_override; /// Frame opacity. Relative to window opacity, also affects shadow /// opacity. double frame_opacity; /// Whether to detect _NET_WM_OPACITY on client windows. Used on window /// managers that don't pass _NET_WM_OPACITY to frame windows. bool detect_client_opacity; /// Step for pregenerating alpha pictures. 0.01 - 1.0. double alpha_step; // === Other window processing === /// Whether to blur background of semi-transparent / ARGB windows. bool blur_background; /// Whether to blur background when the window frame is not opaque. /// Implies blur_background. bool blur_background_frame; /// Whether to use fixed blur strength instead of adjusting according /// to window opacity. bool blur_background_fixed; /// Background blur blacklist. A linked list of conditions. c2_lptr_t *blur_background_blacklist; /// Blur convolution kernel. XFixed *blur_kerns[MAX_BLUR_PASS]; /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. double inactive_dim; /// Whether to use fixed inactive dim opacity, instead of deciding /// based on window opacity. bool inactive_dim_fixed; /// Conditions of windows to have inverted colors. c2_lptr_t *invert_color_list; /// Rules to change window opacity. c2_lptr_t *opacity_rules; // === Focus related === /// Consider windows of specific types to be always focused. bool wintype_focus[NUM_WINTYPES]; /// Whether to try to detect WM windows and mark them as focused. bool mark_wmwin_focused; /// Whether to mark override-redirect windows as focused. bool mark_ovredir_focused; /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. bool use_ewmh_active_win; /// A list of windows always to be considered focused. c2_lptr_t *focus_blacklist; /// Whether to do window grouping with WM_TRANSIENT_FOR. bool detect_transient; /// Whether to do window grouping with WM_CLIENT_LEADER. bool detect_client_leader; // === Calculated === /// Whether compton needs to track focus changes. bool track_focus; /// Whether compton needs to track window name and class. bool track_wdata; /// Whether compton needs to track window leaders. bool track_leader; } options_t; #ifdef CONFIG_VSYNC_OPENGL /// Structure containing GLX-dependent data for a compton session. typedef struct { // === OpenGL related === /// GLX context. GLXContext context; /// Whether we have GL_ARB_texture_non_power_of_two. bool has_texture_non_power_of_two; /// Pointer to glXGetVideoSyncSGI function. f_GetVideoSync glXGetVideoSyncSGI; /// Pointer to glXWaitVideoSyncSGI function. f_WaitVideoSync glXWaitVideoSyncSGI; /// Pointer to glXGetSyncValuesOML function. f_GetSyncValuesOML glXGetSyncValuesOML; /// Pointer to glXWaitForMscOML function. f_WaitForMscOML glXWaitForMscOML; /// Pointer to glXSwapIntervalSGI function. f_SwapIntervalSGI glXSwapIntervalProc; /// Pointer to glXSwapIntervalMESA function. f_SwapIntervalMESA glXSwapIntervalMESAProc; /// Pointer to glXBindTexImageEXT function. f_BindTexImageEXT glXBindTexImageProc; /// Pointer to glXReleaseTexImageEXT function. f_ReleaseTexImageEXT glXReleaseTexImageProc; /// Pointer to glXCopySubBufferMESA function. f_CopySubBuffer glXCopySubBufferProc; #ifdef CONFIG_GLX_SYNC /// Pointer to the glFenceSync() function. f_FenceSync glFenceSyncProc; /// Pointer to the glIsSync() function. f_IsSync glIsSyncProc; /// Pointer to the glDeleteSync() function. f_DeleteSync glDeleteSyncProc; /// Pointer to the glClientWaitSync() function. f_ClientWaitSync glClientWaitSyncProc; /// Pointer to the glWaitSync() function. f_WaitSync glWaitSyncProc; /// Pointer to the glImportSyncEXT() function. f_ImportSyncEXT glImportSyncEXT; #endif #ifdef DEBUG_GLX_MARK /// Pointer to StringMarkerGREMEDY function. f_StringMarkerGREMEDY glStringMarkerGREMEDY; /// Pointer to FrameTerminatorGREMEDY function. f_FrameTerminatorGREMEDY glFrameTerminatorGREMEDY; #endif /// Current GLX Z value. int z; /// FBConfig-s for GLX pixmap of different depths. glx_fbconfig_t *fbconfigs[OPENGL_MAX_DEPTH + 1]; #ifdef CONFIG_VSYNC_OPENGL_GLSL glx_blur_pass_t blur_passes[MAX_BLUR_PASS]; #endif } glx_session_t; #define CGLX_SESSION_INIT { .context = NULL } #endif /// Structure containing all necessary data for a compton session. typedef struct _session_t { // === Display related === /// Display in use. Display *dpy; /// Default screen. int scr; /// Default visual. Visual *vis; /// Default depth. int depth; /// Root window. Window root; /// Height of root window. int root_height; /// Width of root window. int root_width; // Damage of root window. // Damage root_damage; /// X Composite overlay window. Used if --paint-on-overlay. Window overlay; /// Whether the root tile is filled by compton. bool root_tile_fill; /// Picture of the root window background. paint_t root_tile_paint; /// A region of the size of the screen. XserverRegion screen_reg; /// Picture of root window. Destination of painting in no-DBE painting /// mode. Picture root_picture; /// A Picture acting as the painting target. Picture tgt_picture; /// Temporary buffer to paint to before sending to display. paint_t tgt_buffer; #ifdef CONFIG_XSYNC XSyncFence tgt_buffer_fence; #endif /// DBE back buffer for root window. Used in DBE painting mode. XdbeBackBuffer root_dbe; /// Window ID of the window we register as a symbol. Window reg_win; #ifdef CONFIG_VSYNC_OPENGL /// Pointer to GLX data. glx_session_t *psglx; #endif // === Operation related === /// Program options. options_t o; /// File descriptors to check for reading. fd_set *pfds_read; /// File descriptors to check for writing. fd_set *pfds_write; /// File descriptors to check for exceptions. fd_set *pfds_except; /// Largest file descriptor in fd_set-s above. int nfds_max; /// Linked list of all timeouts. struct _timeout_t *tmout_lst; /// Timeout for delayed unredirection. struct _timeout_t *tmout_unredir; /// Whether we have hit unredirection timeout. bool tmout_unredir_hit; /// Whether we have received an event in this cycle. bool ev_received; /// Whether the program is idling. I.e. no fading, no potential window /// changes. bool idling; /// Program start time. struct timeval time_start; /// The region needs to painted on next paint. XserverRegion all_damage; /// The region damaged on the last paint. XserverRegion all_damage_last[CGLX_MAX_BUFFER_AGE]; /// Whether all windows are currently redirected. bool redirected; /// Pre-generated alpha pictures. Picture *alpha_picts; /// Whether all reg_ignore of windows should expire in this paint. bool reg_ignore_expire; /// Time of last fading. In milliseconds. time_ms_t fade_time; /// Head pointer of the error ignore linked list. ignore_t *ignore_head; /// Pointer to the next member of tail element of the error /// ignore linked list. ignore_t **ignore_tail; // Cached blur convolution kernels. XFixed *blur_kerns_cache[MAX_BLUR_PASS]; /// Reset program after next paint. bool reset; // === Expose event related === /// Pointer to an array of XRectangle-s of exposed region. XRectangle *expose_rects; /// Number of XRectangle-s in expose_rects. int size_expose; /// Index of the next free slot in expose_rects. int n_expose; // === Window related === /// Linked list of all windows. struct _win *list; /// Pointer to win of current active window. Used by /// EWMH _NET_ACTIVE_WINDOW focus detection. In theory, /// it's more reliable to store the window ID directly here, just in /// case the WM does something extraordinary, but caching the pointer /// means another layer of complexity. struct _win *active_win; /// Window ID of leader window of currently active window. Used for /// subsidiary window detection. Window active_leader; // === Shadow/dimming related === /// 1x1 black Picture. Picture black_picture; /// 1x1 Picture of the shadow color. Picture cshadow_picture; /// 1x1 white Picture. Picture white_picture; /// Gaussian map of shadow. conv *gaussian_map; // for shadow precomputation /// Shadow depth on one side. int cgsize; /// Pre-computed color table for corners of shadow. unsigned char *shadow_corner; /// Pre-computed color table for a side of shadow. unsigned char *shadow_top; /// A region in which shadow is not painted on. XserverRegion shadow_exclude_reg; // === Software-optimization-related === /// Currently used refresh rate. short refresh_rate; /// Interval between refresh in nanoseconds. long refresh_intv; /// Nanosecond offset of the first painting. long paint_tm_offset; #ifdef CONFIG_VSYNC_DRM // === DRM VSync related === /// File descriptor of DRI device file. Used for DRM VSync. int drm_fd; #endif // === X extension related === /// Event base number for X Fixes extension. int xfixes_event; /// Error base number for X Fixes extension. int xfixes_error; /// Event base number for X Damage extension. int damage_event; /// Error base number for X Damage extension. int damage_error; /// Event base number for X Render extension. int render_event; /// Error base number for X Render extension. int render_error; /// Event base number for X Composite extension. int composite_event; /// Error base number for X Composite extension. int composite_error; /// Major opcode for X Composite extension. int composite_opcode; /// Whether X Composite NameWindowPixmap is available. Aka if X /// Composite version >= 0.2. bool has_name_pixmap; /// Whether X Shape extension exists. bool shape_exists; /// Event base number for X Shape extension. int shape_event; /// Error base number for X Shape extension. int shape_error; /// Whether X RandR extension exists. bool randr_exists; /// Event base number for X RandR extension. int randr_event; /// Error base number for X RandR extension. int randr_error; #ifdef CONFIG_VSYNC_OPENGL /// Whether X GLX extension exists. bool glx_exists; /// Event base number for X GLX extension. int glx_event; /// Error base number for X GLX extension. int glx_error; #endif /// Whether X DBE extension exists. bool dbe_exists; #ifdef CONFIG_XINERAMA /// Whether X Xinerama extension exists. bool xinerama_exists; /// Xinerama screen info. XineramaScreenInfo *xinerama_scrs; /// Xinerama screen regions. XserverRegion *xinerama_scr_regs; /// Number of Xinerama screens. int xinerama_nscrs; #endif #ifdef CONFIG_XSYNC /// Whether X Sync extension exists. bool xsync_exists; /// Event base number for X Sync extension. int xsync_event; /// Error base number for X Sync extension. int xsync_error; #endif /// Whether X Render convolution filter exists. bool xrfilter_convolution_exists; // === Atoms === /// Atom of property _NET_WM_OPACITY. Atom atom_opacity; /// Atom of _NET_FRAME_EXTENTS. Atom atom_frame_extents; /// Property atom to identify top-level frame window. Currently /// WM_STATE. Atom atom_client; /// Atom of property WM_NAME. Atom atom_name; /// Atom of property _NET_WM_NAME. Atom atom_name_ewmh; /// Atom of property WM_CLASS. Atom atom_class; /// Atom of property WM_WINDOW_ROLE. Atom atom_role; /// Atom of property WM_TRANSIENT_FOR. Atom atom_transient; /// Atom of property WM_CLIENT_LEADER. Atom atom_client_leader; /// Atom of property _NET_ACTIVE_WINDOW. Atom atom_ewmh_active_win; /// Atom of property _COMPTON_SHADOW. Atom atom_compton_shadow; /// Atom of property _NET_WM_WINDOW_TYPE. Atom atom_win_type; /// Array of atoms of all possible window types. Atom atoms_wintypes[NUM_WINTYPES]; /// Linked list of additional atoms to track. latom_t *track_atom_lst; #ifdef CONFIG_DBUS // === DBus related === // DBus connection. DBusConnection *dbus_conn; // DBus service name. char *dbus_service; #endif } session_t; /// Structure representing a top-level window compton manages. typedef struct _win { /// Pointer to the next structure in the linked list. struct _win *next; /// Pointer to the next higher window to paint. struct _win *prev_trans; // Core members /// ID of the top-level frame window. Window id; /// Window attributes. XWindowAttributes a; #ifdef CONFIG_XINERAMA /// Xinerama screen this window is on. int xinerama_scr; #endif /// Window visual pict format; XRenderPictFormat *pictfmt; /// Window painting mode. winmode_t mode; /// Whether the window has been damaged at least once. bool damaged; #ifdef CONFIG_XSYNC /// X Sync fence of drawable. XSyncFence fence; #endif /// Whether the window was damaged after last paint. bool pixmap_damaged; /// Damage of the window. Damage damage; /// Paint info of the window. paint_t paint; /// Bounding shape of the window. XserverRegion border_size; /// Region of the whole window, shadow region included. XserverRegion extents; /// Window flags. Definitions above. int_fast16_t flags; /// Whether there's a pending ConfigureNotify happening /// when the window is unmapped. bool need_configure; /// Queued ConfigureNotify when the window is unmapped. XConfigureEvent queue_configure; /// Region to be ignored when painting. Basically the region where /// higher opaque windows will paint upon. Depends on window frame /// opacity state, window geometry, window mapped/unmapped state, /// window mode, of this and all higher windows. XserverRegion reg_ignore; /// Cached width/height of the window including border. int widthb, heightb; /// Whether the window has been destroyed. bool destroyed; /// Whether the window is bounding-shaped. bool bounding_shaped; /// Whether the window just have rounded corners. bool rounded_corners; /// Whether this window is to be painted. bool to_paint; /// Whether the window is painting excluded. bool paint_excluded; /// Whether the window is unredirect-if-possible excluded. bool unredir_if_possible_excluded; /// Whether this window is in open/close state. bool in_openclose; // Client window related members /// ID of the top-level client window of the window. Window client_win; /// Type of the window. wintype_t window_type; /// Whether it looks like a WM window. We consider a window WM window if /// it does not have a decedent with WM_STATE and it is not override- /// redirected itself. bool wmwin; /// Leader window ID of the window. Window leader; /// Cached topmost window ID of the window. Window cache_leader; // Focus-related members /// Whether the window is to be considered focused. bool focused; /// Override value of window focus state. Set by D-Bus method calls. switch_t focused_force; // Blacklist related members /// Name of the window. char *name; /// Window instance class of the window. char *class_instance; /// Window general class of the window. char *class_general; /// WM_WINDOW_ROLE value of the window. char *role; const c2_lptr_t *cache_sblst; const c2_lptr_t *cache_fblst; const c2_lptr_t *cache_fcblst; const c2_lptr_t *cache_ivclst; const c2_lptr_t *cache_bbblst; const c2_lptr_t *cache_oparule; const c2_lptr_t *cache_pblst; const c2_lptr_t *cache_uipblst; // Opacity-related members /// Current window opacity. opacity_t opacity; /// Target window opacity. opacity_t opacity_tgt; /// Cached value of opacity window attribute. opacity_t opacity_prop; /// Cached value of opacity window attribute on client window. For /// broken window managers not transferring client window's /// _NET_WM_OPACITY value opacity_t opacity_prop_client; /// Last window opacity value we set. opacity_t opacity_set; // Fading-related members /// Do not fade if it's false. Change on window type change. /// Used by fading blacklist in the future. bool fade; /// Fade state on last paint. bool fade_last; /// Override value of window fade state. Set by D-Bus method calls. switch_t fade_force; /// Callback to be called after fading completed. void (*fade_callback) (session_t *ps, struct _win *w); // Frame-opacity-related members /// Current window frame opacity. Affected by window opacity. double frame_opacity; /// Frame extents. Acquired from _NET_FRAME_EXTENTS. margin_t frame_extents; // Shadow-related members /// Whether a window has shadow. Calculated. bool shadow; /// Shadow state on last paint. bool shadow_last; /// Override value of window shadow state. Set by D-Bus method calls. switch_t shadow_force; /// Opacity of the shadow. Affected by window opacity and frame opacity. double shadow_opacity; /// X offset of shadow. Affected by commandline argument. int shadow_dx; /// Y offset of shadow. Affected by commandline argument. int shadow_dy; /// Width of shadow. Affected by window size and commandline argument. int shadow_width; /// Height of shadow. Affected by window size and commandline argument. int shadow_height; /// Picture to render shadow. Affected by window size. paint_t shadow_paint; /// The value of _COMPTON_SHADOW attribute of the window. Below 0 for /// none. long prop_shadow; // Dim-related members /// Whether the window is to be dimmed. bool dim; /// Whether to invert window color. bool invert_color; /// Color inversion state on last paint. bool invert_color_last; /// Override value of window color inversion state. Set by D-Bus method /// calls. switch_t invert_color_force; /// Whether to blur window background. bool blur_background; /// Background state on last paint. bool blur_background_last; #ifdef CONFIG_VSYNC_OPENGL_GLSL /// Textures and FBO background blur use. glx_blur_cache_t glx_blur_cache; #endif } win; /// Temporary structure used for communication between /// get_cfg() and parse_config(). struct options_tmp { bool no_dock_shadow; bool no_dnd_shadow; double menu_opacity; }; /// Structure for a recorded timeout. typedef struct _timeout_t { bool enabled; void *data; bool (*callback)(session_t *ps, struct _timeout_t *ptmout); time_ms_t interval; time_ms_t firstrun; time_ms_t lastrun; struct _timeout_t *next; } timeout_t; /// Enumeration for window event hints. typedef enum { WIN_EVMODE_UNKNOWN, WIN_EVMODE_FRAME, WIN_EVMODE_CLIENT } win_evmode_t; extern const char * const WINTYPES[NUM_WINTYPES]; extern const char * const VSYNC_STRS[NUM_VSYNC + 1]; extern const char * const BACKEND_STRS[NUM_BKEND + 1]; extern session_t *ps_g; // == Debugging code == static inline void print_timestamp(session_t *ps); #ifdef DEBUG_BACKTRACE #include #define BACKTRACE_SIZE 25 /** * Print current backtrace. * * Stolen from glibc manual. */ static inline void print_backtrace(void) { void *array[BACKTRACE_SIZE]; size_t size; char **strings; size = backtrace(array, BACKTRACE_SIZE); strings = backtrace_symbols(array, size); for (size_t i = 0; i < size; i++) printf ("%s\n", strings[i]); free(strings); } #ifdef DEBUG_ALLOC_REG /** * Wrapper of XFixesCreateRegion, for debugging. */ static inline XserverRegion XFixesCreateRegion_(Display *dpy, XRectangle *p, int n, const char *func, int line) { XserverRegion reg = XFixesCreateRegion(dpy, p, n); print_timestamp(ps_g); printf("%#010lx: XFixesCreateRegion() in %s():%d\n", reg, func, line); print_backtrace(); fflush(stdout); return reg; } /** * Wrapper of XFixesDestroyRegion, for debugging. */ static inline void XFixesDestroyRegion_(Display *dpy, XserverRegion reg, const char *func, int line) { XFixesDestroyRegion(dpy, reg); print_timestamp(ps_g); printf("%#010lx: XFixesDestroyRegion() in %s():%d\n", reg, func, line); fflush(stdout); } #define XFixesCreateRegion(dpy, p, n) XFixesCreateRegion_(dpy, p, n, __func__, __LINE__) #define XFixesDestroyRegion(dpy, reg) XFixesDestroyRegion_(dpy, reg, __func__, __LINE__) #endif #endif // === Functions === /** * @brief Quit if the passed-in pointer is empty. */ static inline void * allocchk_(const char *func_name, void *ptr) { if (!ptr) { printf_err("%s(): Failed to allocate memory.", func_name); exit(1); } return ptr; } /// @brief Wrapper of allocchk_(). #define allocchk(ptr) allocchk_(__func__, ptr) /// @brief Wrapper of malloc(). #define cmalloc(nmemb, type) ((type *) allocchk(malloc((nmemb) * sizeof(type)))) /// @brief Wrapper of calloc(). #define ccalloc(nmemb, type) ((type *) allocchk(calloc((nmemb), sizeof(type)))) /// @brief Wrapper of ealloc(). #define crealloc(ptr, nmemb, type) ((type *) allocchk(realloc((ptr), (nmemb) * sizeof(type)))) /// @brief Zero out the given memory block. #define cmemzero(ptr, size) memset((ptr), 0, (size)) /// @brief Wrapper of cmemzero() that handles a pointer to a single item, for /// convenience. #define cmemzero_one(ptr) cmemzero((ptr), sizeof(*(ptr))) /** * Return whether a struct timeval value is empty. */ static inline bool timeval_isempty(struct timeval *ptv) { if (!ptv) return false; return ptv->tv_sec <= 0 && ptv->tv_usec <= 0; } /** * Compare a struct timeval with a time in milliseconds. * * @return > 0 if ptv > ms, 0 if ptv == 0, -1 if ptv < ms */ static inline int timeval_ms_cmp(struct timeval *ptv, time_ms_t ms) { assert(ptv); // We use those if statement instead of a - expression because of possible // truncation problem from long to int. { long sec = ms / MS_PER_SEC; if (ptv->tv_sec > sec) return 1; if (ptv->tv_sec < sec) return -1; } { long usec = ms % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC); if (ptv->tv_usec > usec) return 1; if (ptv->tv_usec < usec) return -1; } return 0; } /** * Subtracting two struct timeval values. * * Taken from glibc manual. * * Subtract the `struct timeval' values X and Y, * storing the result in RESULT. * Return 1 if the difference is negative, otherwise 0. */ static inline int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) { /* Perform the carry for the later subtraction by updating y. */ if (x->tv_usec < y->tv_usec) { long nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } if (x->tv_usec - y->tv_usec > 1000000) { long nsec = (x->tv_usec - y->tv_usec) / 1000000; y->tv_usec += 1000000 * nsec; y->tv_sec -= nsec; } /* Compute the time remaining to wait. tv_usec is certainly positive. */ result->tv_sec = x->tv_sec - y->tv_sec; result->tv_usec = x->tv_usec - y->tv_usec; /* Return 1 if result is negative. */ return x->tv_sec < y->tv_sec; } /** * Subtracting two struct timespec values. * * Taken from glibc manual. * * Subtract the `struct timespec' values X and Y, * storing the result in RESULT. * Return 1 if the difference is negative, otherwise 0. */ static inline int timespec_subtract(struct timespec *result, struct timespec *x, struct timespec *y) { /* Perform the carry for the later subtraction by updating y. */ if (x->tv_nsec < y->tv_nsec) { long nsec = (y->tv_nsec - x->tv_nsec) / NS_PER_SEC + 1; y->tv_nsec -= NS_PER_SEC * nsec; y->tv_sec += nsec; } if (x->tv_nsec - y->tv_nsec > NS_PER_SEC) { long nsec = (x->tv_nsec - y->tv_nsec) / NS_PER_SEC; y->tv_nsec += NS_PER_SEC * nsec; y->tv_sec -= nsec; } /* Compute the time remaining to wait. tv_nsec is certainly positive. */ result->tv_sec = x->tv_sec - y->tv_sec; result->tv_nsec = x->tv_nsec - y->tv_nsec; /* Return 1 if result is negative. */ return x->tv_sec < y->tv_sec; } /** * Get current time in struct timeval. */ static inline struct timeval get_time_timeval(void) { struct timeval tv = { 0, 0 }; gettimeofday(&tv, NULL); // Return a time of all 0 if the call fails return tv; } /** * Get current time in struct timespec. * * Note its starting time is unspecified. */ static inline struct timespec get_time_timespec(void) { struct timespec tm = { 0, 0 }; clock_gettime(CLOCK_MONOTONIC, &tm); // Return a time of all 0 if the call fails return tm; } /** * Print time passed since program starts execution. * * Used for debugging. */ static inline void print_timestamp(session_t *ps) { struct timeval tm, diff; if (gettimeofday(&tm, NULL)) return; timeval_subtract(&diff, &tm, &ps->time_start); printf("[ %5ld.%02ld ] ", diff.tv_sec, diff.tv_usec / 10000); } /** * Allocate the space and copy a string. */ static inline char * mstrcpy(const char *src) { char *str = cmalloc(strlen(src) + 1, char); strcpy(str, src); return str; } /** * Allocate the space and copy a string. */ static inline char * mstrncpy(const char *src, unsigned len) { char *str = cmalloc(len + 1, char); strncpy(str, src, len); str[len] = '\0'; return str; } /** * Allocate the space and join two strings. */ static inline char * mstrjoin(const char *src1, const char *src2) { char *str = cmalloc(strlen(src1) + strlen(src2) + 1, char); strcpy(str, src1); strcat(str, src2); return str; } /** * Allocate the space and join two strings; */ static inline char * mstrjoin3(const char *src1, const char *src2, const char *src3) { char *str = cmalloc(strlen(src1) + strlen(src2) + strlen(src3) + 1, char); strcpy(str, src1); strcat(str, src2); strcat(str, src3); return str; } /** * Concatenate a string on heap with another string. */ static inline void mstrextend(char **psrc1, const char *src2) { *psrc1 = crealloc(*psrc1, (*psrc1 ? strlen(*psrc1): 0) + strlen(src2) + 1, char); strcat(*psrc1, src2); } /** * Normalize an int value to a specific range. * * @param i int value to normalize * @param min minimal value * @param max maximum value * @return normalized value */ static inline int __attribute__((const)) normalize_i_range(int i, int min, int max) { if (i > max) return max; if (i < min) return min; return i; } /** * Select the larger integer of two. */ static inline int __attribute__((const)) max_i(int a, int b) { return (a > b ? a : b); } /** * Select the smaller integer of two. */ static inline int __attribute__((const)) min_i(int a, int b) { return (a > b ? b : a); } /** * Select the larger long integer of two. */ static inline long __attribute__((const)) max_l(long a, long b) { return (a > b ? a : b); } /** * Select the smaller long integer of two. */ static inline long __attribute__((const)) min_l(long a, long b) { return (a > b ? b : a); } /** * Normalize a double value to a specific range. * * @param d double value to normalize * @param min minimal value * @param max maximum value * @return normalized value */ static inline double __attribute__((const)) normalize_d_range(double d, double min, double max) { if (d > max) return max; if (d < min) return min; return d; } /** * Normalize a double value to 0.\ 0 - 1.\ 0. * * @param d double value to normalize * @return normalized value */ static inline double __attribute__((const)) normalize_d(double d) { return normalize_d_range(d, 0.0, 1.0); } /** * Parse a VSync option argument. */ static inline bool parse_vsync(session_t *ps, const char *str) { for (vsync_t i = 0; VSYNC_STRS[i]; ++i) if (!strcasecmp(str, VSYNC_STRS[i])) { ps->o.vsync = i; return true; } printf_errf("(\"%s\"): Invalid vsync argument.", str); return false; } /** * Parse a backend option argument. */ static inline bool parse_backend(session_t *ps, const char *str) { for (enum backend i = 0; BACKEND_STRS[i]; ++i) if (!strcasecmp(str, BACKEND_STRS[i])) { ps->o.backend = i; return true; } // Keep compatibility with an old revision containing a spelling mistake... if (!strcasecmp(str, "xr_glx_hybird")) { ps->o.backend = BKEND_XR_GLX_HYBRID; return true; } // cju wants to use dashes if (!strcasecmp(str, "xr-glx-hybrid")) { ps->o.backend = BKEND_XR_GLX_HYBRID; return true; } printf_errf("(\"%s\"): Invalid backend argument.", str); return false; } /** * Parse a glx_swap_method option argument. */ static inline bool parse_glx_swap_method(session_t *ps, const char *str) { // Parse alias if (!strcmp("undefined", str)) { ps->o.glx_swap_method = 0; return true; } if (!strcmp("copy", str)) { ps->o.glx_swap_method = 1; return true; } if (!strcmp("exchange", str)) { ps->o.glx_swap_method = 2; return true; } if (!strcmp("buffer-age", str)) { ps->o.glx_swap_method = -1; return true; } // Parse number { char *pc = NULL; int age = strtol(str, &pc, 0); if (!pc || str == pc) { printf_errf("(\"%s\"): Invalid number.", str); return false; } for (; *pc; ++pc) if (!isspace(*pc)) { printf_errf("(\"%s\"): Trailing characters.", str); return false; } if (age > CGLX_MAX_BUFFER_AGE + 1 || age < -1) { printf_errf("(\"%s\"): Number too large / too small.", str); return false; } ps->o.glx_swap_method = age; } return true; } timeout_t * timeout_insert(session_t *ps, time_ms_t interval, bool (*callback)(session_t *ps, timeout_t *ptmout), void *data); void timeout_invoke(session_t *ps, timeout_t *ptmout); bool timeout_drop(session_t *ps, timeout_t *prm); void timeout_reset(session_t *ps, timeout_t *ptmout); /** * Add a file descriptor to a select() fd_set. */ static inline bool fds_insert_select(fd_set **ppfds, int fd) { assert(fd <= FD_SETSIZE); if (!*ppfds) { if ((*ppfds = malloc(sizeof(fd_set)))) { FD_ZERO(*ppfds); } else { fprintf(stderr, "Failed to allocate memory for select() fdset.\n"); exit(1); } } FD_SET(fd, *ppfds); return true; } /** * Add a new file descriptor to wait for. */ static inline bool fds_insert(session_t *ps, int fd, short events) { bool result = true; ps->nfds_max = max_i(fd + 1, ps->nfds_max); if (POLLIN & events) result = fds_insert_select(&ps->pfds_read, fd) && result; if (POLLOUT & events) result = fds_insert_select(&ps->pfds_write, fd) && result; if (POLLPRI & events) result = fds_insert_select(&ps->pfds_except, fd) && result; return result; } /** * Delete a file descriptor to wait for. */ static inline void fds_drop(session_t *ps, int fd, short events) { // Drop fd from respective fd_set-s if (POLLIN & events && ps->pfds_read) FD_CLR(fd, ps->pfds_read); if (POLLOUT & events && ps->pfds_write) FD_CLR(fd, ps->pfds_write); if (POLLPRI & events && ps->pfds_except) FD_CLR(fd, ps->pfds_except); } #define CPY_FDS(key) \ fd_set * key = NULL; \ if (ps->key) { \ key = malloc(sizeof(fd_set)); \ memcpy(key, ps->key, sizeof(fd_set)); \ if (!key) { \ fprintf(stderr, "Failed to allocate memory for copying select() fdset.\n"); \ exit(1); \ } \ } \ /** * Poll for changes. * * poll() is much better than select(), but ppoll() does not exist on * *BSD. */ static inline int fds_poll(session_t *ps, struct timeval *ptv) { // Copy fds CPY_FDS(pfds_read); CPY_FDS(pfds_write); CPY_FDS(pfds_except); int ret = select(ps->nfds_max, pfds_read, pfds_write, pfds_except, ptv); free(pfds_read); free(pfds_write); free(pfds_except); return ret; } #undef CPY_FDS /** * Wrapper of XFree() for convenience. * * Because a NULL pointer cannot be passed to XFree(), its man page says. */ static inline void cxfree(void *data) { if (data) XFree(data); } /** * Wrapper of XInternAtom() for convenience. */ static inline Atom get_atom(session_t *ps, const char *atom_name) { return XInternAtom(ps->dpy, atom_name, False); } /** * Return the painting target window. */ static inline Window get_tgt_window(session_t *ps) { return ps->o.paint_on_overlay ? ps->overlay: ps->root; } /** * Find a window from window id in window linked list of the session. */ static inline win * find_win(session_t *ps, Window id) { if (!id) return NULL; win *w; for (w = ps->list; w; w = w->next) { if (w->id == id && !w->destroyed) return w; } return 0; } /** * Find out the WM frame of a client window using existing data. * * @param id window ID * @return struct _win object of the found window, NULL if not found */ static inline win * find_toplevel(session_t *ps, Window id) { if (!id) return NULL; for (win *w = ps->list; w; w = w->next) { if (w->client_win == id && !w->destroyed) return w; } return NULL; } /** * Check if current backend uses XRender for rendering. */ static inline bool bkend_use_xrender(session_t *ps) { return BKEND_XRENDER == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend; } /** * Check if current backend uses GLX. */ static inline bool bkend_use_glx(session_t *ps) { return BKEND_GLX == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend; } /** * Check if there's a GLX context. */ static inline bool glx_has_context(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL return ps->psglx && ps->psglx->context; #else return false; #endif } /** * Check if a window is really focused. */ static inline bool win_is_focused_real(session_t *ps, const win *w) { return IsViewable == w->a.map_state && ps->active_win == w; } /** * Find out the currently focused window. * * @return struct _win object of the found window, NULL if not found */ static inline win * find_focused(session_t *ps) { if (!ps->o.track_focus) return NULL; if (ps->active_win && win_is_focused_real(ps, ps->active_win)) return ps->active_win; return NULL; } /** * Copies a region. */ static inline XserverRegion copy_region(const session_t *ps, XserverRegion oldregion) { if (!oldregion) return None; XserverRegion region = XFixesCreateRegion(ps->dpy, NULL, 0); XFixesCopyRegion(ps->dpy, region, oldregion); return region; } /** * Destroy a XserverRegion. */ static inline void free_region(session_t *ps, XserverRegion *p) { if (*p) { XFixesDestroyRegion(ps->dpy, *p); *p = None; } } /** * Free all regions in ps->all_damage_last . */ static inline void free_all_damage_last(session_t *ps) { for (int i = 0; i < CGLX_MAX_BUFFER_AGE; ++i) free_region(ps, &ps->all_damage_last[i]); } #ifdef CONFIG_XSYNC /** * Free a XSync fence. */ static inline void free_fence(session_t *ps, XSyncFence *pfence) { if (*pfence) XSyncDestroyFence(ps->dpy, *pfence); *pfence = None; } #else #define free_fence(ps, pfence) ((void) 0) #endif /** * Crop a rectangle by another rectangle. * * psrc and pdst cannot be the same. */ static inline void rect_crop(XRectangle *pdst, const XRectangle *psrc, const XRectangle *pbound) { assert(psrc != pdst); pdst->x = max_i(psrc->x, pbound->x); pdst->y = max_i(psrc->y, pbound->y); pdst->width = max_i(0, min_i(psrc->x + psrc->width, pbound->x + pbound->width) - pdst->x); pdst->height = max_i(0, min_i(psrc->y + psrc->height, pbound->y + pbound->height) - pdst->y); } /** * Check if a rectangle includes the whole screen. */ static inline bool rect_is_fullscreen(session_t *ps, int x, int y, unsigned wid, unsigned hei) { return (x <= 0 && y <= 0 && (x + wid) >= ps->root_width && (y + hei) >= ps->root_height); } /** * Check if a window is a fullscreen window. * * It's not using w->border_size for performance measures. */ static inline bool win_is_fullscreen(session_t *ps, const win *w) { return rect_is_fullscreen(ps, w->a.x, w->a.y, w->widthb, w->heightb) && (!w->bounding_shaped || w->rounded_corners); } /** * Check if a window will be painted solid. */ static inline bool win_is_solid(session_t *ps, const win *w) { return WMODE_SOLID == w->mode && !ps->o.force_win_blend; } /** * Determine if a window has a specific property. * * @param ps current session * @param w window to check * @param atom atom of property to check * @return 1 if it has the attribute, 0 otherwise */ static inline bool wid_has_prop(const session_t *ps, Window w, Atom atom) { Atom type = None; int format; unsigned long nitems, after; unsigned char *data; if (Success == XGetWindowProperty(ps->dpy, w, atom, 0, 0, False, AnyPropertyType, &type, &format, &nitems, &after, &data)) { cxfree(data); if (type) return true; } return false; } winprop_t wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, long length, Atom rtype, int rformat); /** * Wrapper of wid_get_prop_adv(). */ static inline winprop_t wid_get_prop(const session_t *ps, Window wid, Atom atom, long length, Atom rtype, int rformat) { return wid_get_prop_adv(ps, wid, atom, 0L, length, rtype, rformat); } /** * Get the numeric property value from a win_prop_t. */ static inline long winprop_get_int(winprop_t prop) { long tgt = 0; if (!prop.nitems) return 0; switch (prop.format) { case 8: tgt = *(prop.data.p8); break; case 16: tgt = *(prop.data.p16); break; case 32: tgt = *(prop.data.p32); break; default: assert(0); break; } return tgt; } bool wid_get_text_prop(session_t *ps, Window wid, Atom prop, char ***pstrlst, int *pnstr); /** * Free a winprop_t. * * @param pprop pointer to the winprop_t to free. */ static inline void free_winprop(winprop_t *pprop) { // Empty the whole structure to avoid possible issues if (pprop->data.p8) { cxfree(pprop->data.p8); pprop->data.p8 = NULL; } pprop->nitems = 0; } void force_repaint(session_t *ps); bool vsync_init(session_t *ps); void vsync_deinit(session_t *ps); #ifdef CONFIG_VSYNC_OPENGL /** @name GLX */ ///@{ #ifdef CONFIG_GLX_SYNC void xr_glx_sync(session_t *ps, Drawable d, XSyncFence *pfence); #endif bool glx_init(session_t *ps, bool need_render); void glx_destroy(session_t *ps); bool glx_reinit(session_t *ps, bool need_render); void glx_on_root_change(session_t *ps); bool glx_init_blur(session_t *ps); #ifdef CONFIG_VSYNC_OPENGL_GLSL bool glx_load_prog_main(session_t *ps, const char *vshader_str, const char *fshader_str, glx_prog_main_t *pprogram); #endif bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, unsigned width, unsigned height, unsigned depth); void glx_release_pixmap(session_t *ps, glx_texture_t *ptex); void glx_paint_pre(session_t *ps, XserverRegion *preg); /** * Check if a texture is binded, or is binded to the given pixmap. */ static inline bool glx_tex_binded(const glx_texture_t *ptex, Pixmap pixmap) { return ptex && ptex->glpixmap && ptex->texture && (!pixmap || pixmap == ptex->pixmap); } void glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg); #ifdef CONFIG_VSYNC_OPENGL_GLSL bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, GLfloat factor_center, XserverRegion reg_tgt, const reg_data_t *pcache_reg, glx_blur_cache_t *pbc); #endif bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg); bool glx_render_(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, double opacity, bool argb, bool neg, XserverRegion reg_tgt, const reg_data_t *pcache_reg #ifdef CONFIG_VSYNC_OPENGL_GLSL , const glx_prog_main_t *pprogram #endif ); #ifdef CONFIG_VSYNC_OPENGL_GLSL #define \ glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) \ glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) #else #define \ glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) \ glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg) #endif void glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg); unsigned char * glx_take_screenshot(session_t *ps, int *out_length); #ifdef CONFIG_VSYNC_OPENGL_GLSL GLuint glx_create_shader(GLenum shader_type, const char *shader_str); GLuint glx_create_program(const GLuint * const shaders, int nshaders); GLuint glx_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str); #endif /** * Free a GLX texture. */ static inline void free_texture_r(session_t *ps, GLuint *ptexture) { if (*ptexture) { assert(glx_has_context(ps)); glDeleteTextures(1, ptexture); *ptexture = 0; } } /** * Free a GLX Framebuffer object. */ static inline void free_glx_fbo(session_t *ps, GLuint *pfbo) { #ifdef CONFIG_VSYNC_OPENGL_FBO if (*pfbo) { glDeleteFramebuffers(1, pfbo); *pfbo = 0; } #endif assert(!*pfbo); } #ifdef CONFIG_VSYNC_OPENGL_GLSL /** * Free data in glx_blur_cache_t on resize. */ static inline void free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) { free_texture_r(ps, &pbc->textures[0]); free_texture_r(ps, &pbc->textures[1]); pbc->width = 0; pbc->height = 0; } /** * Free a glx_blur_cache_t */ static inline void free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) { free_glx_fbo(ps, &pbc->fbo); free_glx_bc_resize(ps, pbc); } #endif #endif /** * Free a glx_texture_t. */ static inline void free_texture(session_t *ps, glx_texture_t **pptex) { glx_texture_t *ptex = *pptex; // Quit if there's nothing if (!ptex) return; #ifdef CONFIG_VSYNC_OPENGL glx_release_pixmap(ps, ptex); free_texture_r(ps, &ptex->texture); // Free structure itself free(ptex); *pptex = NULL; #endif assert(!*pptex); } /** * Free GLX part of paint_t. */ static inline void free_paint_glx(session_t *ps, paint_t *ppaint) { free_texture(ps, &ppaint->ptex); } /** * Free GLX part of win. */ static inline void free_win_res_glx(session_t *ps, win *w) { free_paint_glx(ps, &w->paint); free_paint_glx(ps, &w->shadow_paint); #ifdef CONFIG_VSYNC_OPENGL_GLSL free_glx_bc(ps, &w->glx_blur_cache); #endif } /** * Add a OpenGL debugging marker. */ static inline void glx_mark_(session_t *ps, const char *func, XID xid, bool start) { #ifdef DEBUG_GLX_MARK if (glx_has_context(ps) && ps->psglx->glStringMarkerGREMEDY) { if (!func) func = "(unknown)"; const char *postfix = (start ? " (start)": " (end)"); char *str = malloc((strlen(func) + 12 + 2 + strlen(postfix) + 5) * sizeof(char)); strcpy(str, func); sprintf(str + strlen(str), "(%#010lx)%s", xid, postfix); ps->psglx->glStringMarkerGREMEDY(strlen(str), str); free(str); } #endif } #define glx_mark(ps, xid, start) glx_mark_(ps, __func__, xid, start) /** * Add a OpenGL debugging marker. */ static inline void glx_mark_frame(session_t *ps) { #ifdef DEBUG_GLX_MARK if (glx_has_context(ps) && ps->psglx->glFrameTerminatorGREMEDY) ps->psglx->glFrameTerminatorGREMEDY(); #endif } ///@} #ifdef CONFIG_XSYNC #define xr_sync(ps, d, pfence) xr_sync_(ps, d, pfence) #else #define xr_sync(ps, d, pfence) xr_sync_(ps, d) #endif /** * Synchronizes a X Render drawable to ensure all pending painting requests * are completed. */ static inline void xr_sync_(session_t *ps, Drawable d #ifdef CONFIG_XSYNC , XSyncFence *pfence #endif ) { if (!ps->o.xrender_sync) return; XSync(ps->dpy, False); #ifdef CONFIG_XSYNC if (ps->o.xrender_sync_fence && ps->xsync_exists) { // TODO: If everybody just follows the rules stated in X Sync prototype, // we need only one fence per screen, but let's stay a bit cautious right // now XSyncFence tmp_fence = None; if (!pfence) pfence = &tmp_fence; assert(pfence); if (!*pfence) *pfence = XSyncCreateFence(ps->dpy, d, False); if (*pfence) { Bool triggered = False; /* if (XSyncQueryFence(ps->dpy, *pfence, &triggered) && triggered) XSyncResetFence(ps->dpy, *pfence); */ // The fence may fail to be created (e.g. because of died drawable) assert(!XSyncQueryFence(ps->dpy, *pfence, &triggered) || !triggered); XSyncTriggerFence(ps->dpy, *pfence); XSyncAwaitFence(ps->dpy, pfence, 1); assert(!XSyncQueryFence(ps->dpy, *pfence, &triggered) || triggered); } else { printf_errf("(%#010lx): Failed to create X Sync fence.", d); } free_fence(ps, &tmp_fence); if (*pfence) XSyncResetFence(ps->dpy, *pfence); } #endif #ifdef CONFIG_GLX_SYNC xr_glx_sync(ps, d, pfence); #endif } /** @name DBus handling */ ///@{ #ifdef CONFIG_DBUS /** @name DBus handling */ ///@{ bool cdbus_init(session_t *ps); void cdbus_destroy(session_t *ps); void cdbus_loop(session_t *ps); void cdbus_ev_win_added(session_t *ps, win *w); void cdbus_ev_win_destroyed(session_t *ps, win *w); void cdbus_ev_win_mapped(session_t *ps, win *w); void cdbus_ev_win_unmapped(session_t *ps, win *w); void cdbus_ev_win_focusout(session_t *ps, win *w); void cdbus_ev_win_focusin(session_t *ps, win *w); //!@} /** @name DBus hooks */ ///@{ void win_set_shadow_force(session_t *ps, win *w, switch_t val); void win_set_fade_force(session_t *ps, win *w, switch_t val); void win_set_focused_force(session_t *ps, win *w, switch_t val); void win_set_invert_color_force(session_t *ps, win *w, switch_t val); void opts_init_track_focus(session_t *ps); void opts_set_no_fading_openclose(session_t *ps, bool newval); //!@} #endif #ifdef CONFIG_C2 /** @name c2 */ ///@{ c2_lptr_t * c2_parsed(session_t *ps, c2_lptr_t **pcondlst, const char *pattern, void *data); #define c2_parse(ps, pcondlst, pattern) c2_parsed((ps), (pcondlst), (pattern), NULL) c2_lptr_t * c2_free_lptr(c2_lptr_t *lp); bool c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst, const c2_lptr_t **cache, void **pdata); #define c2_match(ps, w, condlst, cache) c2_matchd((ps), (w), (condlst), \ (cache), NULL) #endif ///@} /** * @brief Dump the given data to a file. */ static inline bool write_binary_data(const char *path, const unsigned char *data, int length) { if (!data) return false; FILE *f = fopen(path, "wb"); if (!f) { printf_errf("(\"%s\"): Failed to open file for writing.", path); return false; } int wrote_len = fwrite(data, sizeof(unsigned char), length, f); fclose(f); if (wrote_len != length) { printf_errf("(\"%s\"): Failed to write all blocks: %d / %d", path, wrote_len, length); return false; } return true; } /** * @brief Dump raw bytes in HEX format. * * @param data pointer to raw data * @param len length of data */ static inline void hexdump(const char *data, int len) { static const int BYTE_PER_LN = 16; if (len <= 0) return; // Print header printf("%10s:", "Offset"); for (int i = 0; i < BYTE_PER_LN; ++i) printf(" %2d", i); putchar('\n'); // Dump content for (int offset = 0; offset < len; ++offset) { if (!(offset % BYTE_PER_LN)) printf("0x%08x:", offset); printf(" %02hhx", data[offset]); if ((BYTE_PER_LN - 1) == offset % BYTE_PER_LN) putchar('\n'); } if (len % BYTE_PER_LN) putchar('\n'); fflush(stdout); } #endif compton-0.1~beta2+20150922/src/compton.c000066400000000000000000006404671260012102400174640ustar00rootroot00000000000000/* * Compton - a compositor for X11 * * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * * Copyright (c) 2011-2013, Christopher Jeffrey * See LICENSE for more information. * */ #include "compton.h" #include // === Global constants === /// Name strings for window types. const char * const WINTYPES[NUM_WINTYPES] = { "unknown", "desktop", "dock", "toolbar", "menu", "utility", "splash", "dialog", "normal", "dropdown_menu", "popup_menu", "tooltip", "notify", "combo", "dnd", }; /// Names of VSync modes. const char * const VSYNC_STRS[NUM_VSYNC + 1] = { "none", // VSYNC_NONE "drm", // VSYNC_DRM "opengl", // VSYNC_OPENGL "opengl-oml", // VSYNC_OPENGL_OML "opengl-swc", // VSYNC_OPENGL_SWC "opengl-mswc", // VSYNC_OPENGL_MSWC NULL }; /// Names of backends. const char * const BACKEND_STRS[NUM_BKEND + 1] = { "xrender", // BKEND_XRENDER "glx", // BKEND_GLX "xr_glx_hybrid",// BKEND_XR_GLX_HYBRID NULL }; /// Function pointers to init VSync modes. static bool (* const (VSYNC_FUNCS_INIT[NUM_VSYNC]))(session_t *ps) = { [VSYNC_DRM ] = vsync_drm_init, [VSYNC_OPENGL ] = vsync_opengl_init, [VSYNC_OPENGL_OML ] = vsync_opengl_oml_init, [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_init, [VSYNC_OPENGL_MSWC ] = vsync_opengl_mswc_init, }; /// Function pointers to wait for VSync. static int (* const (VSYNC_FUNCS_WAIT[NUM_VSYNC]))(session_t *ps) = { #ifdef CONFIG_VSYNC_DRM [VSYNC_DRM ] = vsync_drm_wait, #endif #ifdef CONFIG_VSYNC_OPENGL [VSYNC_OPENGL ] = vsync_opengl_wait, [VSYNC_OPENGL_OML ] = vsync_opengl_oml_wait, #endif }; /// Function pointers to deinitialize VSync. static void (* const (VSYNC_FUNCS_DEINIT[NUM_VSYNC]))(session_t *ps) = { #ifdef CONFIG_VSYNC_OPENGL [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_deinit, [VSYNC_OPENGL_MSWC ] = vsync_opengl_mswc_deinit, #endif }; /// Names of root window properties that could point to a pixmap of /// background. const static char *background_props_str[] = { "_XROOTPMAP_ID", "_XSETROOT_ID", 0, }; // === Global variables === /// Pointer to current session, as a global variable. Only used by /// error() and reset_enable(), which could not /// have a pointer to current session passed in. session_t *ps_g = NULL; // === Fading === /** * Get the time left before next fading point. * * In milliseconds. */ static int fade_timeout(session_t *ps) { int diff = ps->o.fade_delta - get_time_ms() + ps->fade_time; diff = normalize_i_range(diff, 0, ps->o.fade_delta * 2); return diff; } /** * Run fading on a window. * * @param steps steps of fading */ static void run_fade(session_t *ps, win *w, unsigned steps) { // If we have reached target opacity, return if (w->opacity == w->opacity_tgt) { return; } if (!w->fade) w->opacity = w->opacity_tgt; else if (steps) { // Use double below because opacity_t will probably overflow during // calculations if (w->opacity < w->opacity_tgt) w->opacity = normalize_d_range( (double) w->opacity + (double) ps->o.fade_in_step * steps, 0.0, w->opacity_tgt); else w->opacity = normalize_d_range( (double) w->opacity - (double) ps->o.fade_out_step * steps, w->opacity_tgt, OPAQUE); } if (w->opacity != w->opacity_tgt) { ps->idling = false; } } /** * Set fade callback of a window, and possibly execute the previous * callback. * * @param exec_callback whether the previous callback is to be executed */ static void set_fade_callback(session_t *ps, win *w, void (*callback) (session_t *ps, win *w), bool exec_callback) { void (*old_callback) (session_t *ps, win *w) = w->fade_callback; w->fade_callback = callback; // Must be the last line as the callback could destroy w! if (exec_callback && old_callback) { old_callback(ps, w); // Although currently no callback function affects window state on // next paint, it could, in the future ps->idling = false; } } // === Shadows === static double __attribute__((const)) gaussian(double r, double x, double y) { return ((1 / (sqrt(2 * M_PI * r))) * exp((- (x * x + y * y)) / (2 * r * r))); } static conv * make_gaussian_map(double r) { conv *c; int size = ((int) ceil((r * 3)) + 1) & ~1; int center = size / 2; int x, y; double t; double g; c = malloc(sizeof(conv) + size * size * sizeof(double)); c->size = size; c->data = (double *) (c + 1); t = 0.0; for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { g = gaussian(r, x - center, y - center); t += g; c->data[y * size + x] = g; } } for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { c->data[y * size + x] /= t; } } return c; } /* * A picture will help * * -center 0 width width+center * -center +-----+-------------------+-----+ * | | | | * | | | | * 0 +-----+-------------------+-----+ * | | | | * | | | | * | | | | * height +-----+-------------------+-----+ * | | | | * height+ | | | | * center +-----+-------------------+-----+ */ static unsigned char sum_gaussian(conv *map, double opacity, int x, int y, int width, int height) { int fx, fy; double *g_data; double *g_line = map->data; int g_size = map->size; int center = g_size / 2; int fx_start, fx_end; int fy_start, fy_end; double v; /* * Compute set of filter values which are "in range", * that's the set with: * 0 <= x + (fx-center) && x + (fx-center) < width && * 0 <= y + (fy-center) && y + (fy-center) < height * * 0 <= x + (fx - center) x + fx - center < width * center - x <= fx fx < width + center - x */ fx_start = center - x; if (fx_start < 0) fx_start = 0; fx_end = width + center - x; if (fx_end > g_size) fx_end = g_size; fy_start = center - y; if (fy_start < 0) fy_start = 0; fy_end = height + center - y; if (fy_end > g_size) fy_end = g_size; g_line = g_line + fy_start * g_size + fx_start; v = 0; for (fy = fy_start; fy < fy_end; fy++) { g_data = g_line; g_line += g_size; for (fx = fx_start; fx < fx_end; fx++) { v += *g_data++; } } if (v > 1) v = 1; return ((unsigned char) (v * opacity * 255.0)); } /* precompute shadow corners and sides to save time for large windows */ static void presum_gaussian(session_t *ps, conv *map) { int center = map->size / 2; int opacity, x, y; ps->cgsize = map->size; if (ps->shadow_corner) free(ps->shadow_corner); if (ps->shadow_top) free(ps->shadow_top); ps->shadow_corner = malloc((ps->cgsize + 1) * (ps->cgsize + 1) * 26); ps->shadow_top = malloc((ps->cgsize + 1) * 26); for (x = 0; x <= ps->cgsize; x++) { ps->shadow_top[25 * (ps->cgsize + 1) + x] = sum_gaussian(map, 1, x - center, center, ps->cgsize * 2, ps->cgsize * 2); for (opacity = 0; opacity < 25; opacity++) { ps->shadow_top[opacity * (ps->cgsize + 1) + x] = ps->shadow_top[25 * (ps->cgsize + 1) + x] * opacity / 25; } for (y = 0; y <= x; y++) { ps->shadow_corner[25 * (ps->cgsize + 1) * (ps->cgsize + 1) + y * (ps->cgsize + 1) + x] = sum_gaussian(map, 1, x - center, y - center, ps->cgsize * 2, ps->cgsize * 2); ps->shadow_corner[25 * (ps->cgsize + 1) * (ps->cgsize + 1) + x * (ps->cgsize + 1) + y] = ps->shadow_corner[25 * (ps->cgsize + 1) * (ps->cgsize + 1) + y * (ps->cgsize + 1) + x]; for (opacity = 0; opacity < 25; opacity++) { ps->shadow_corner[opacity * (ps->cgsize + 1) * (ps->cgsize + 1) + y * (ps->cgsize + 1) + x] = ps->shadow_corner[opacity * (ps->cgsize + 1) * (ps->cgsize + 1) + x * (ps->cgsize + 1) + y] = ps->shadow_corner[25 * (ps->cgsize + 1) * (ps->cgsize + 1) + y * (ps->cgsize + 1) + x] * opacity / 25; } } } } static XImage * make_shadow(session_t *ps, double opacity, int width, int height) { XImage *ximage; unsigned char *data; int ylimit, xlimit; int swidth = width + ps->cgsize; int sheight = height + ps->cgsize; int center = ps->cgsize / 2; int x, y; unsigned char d; int x_diff; int opacity_int = (int)(opacity * 25); data = malloc(swidth * sheight * sizeof(unsigned char)); if (!data) return 0; ximage = XCreateImage(ps->dpy, ps->vis, 8, ZPixmap, 0, (char *) data, swidth, sheight, 8, swidth * sizeof(char)); if (!ximage) { free(data); return 0; } /* * Build the gaussian in sections */ /* * center (fill the complete data array) */ // If clear_shadow is enabled and the border & corner shadow (which // later will be filled) could entirely cover the area of the shadow // that will be displayed, do not bother filling other pixels. If it // can't, we must fill the other pixels here. /* if (!(clear_shadow && ps->o.shadow_offset_x <= 0 && ps->o.shadow_offset_x >= -ps->cgsize && ps->o.shadow_offset_y <= 0 && ps->o.shadow_offset_y >= -ps->cgsize)) { */ if (ps->cgsize > 0) { d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + ps->cgsize]; } else { d = sum_gaussian(ps->gaussian_map, opacity, center, center, width, height); } memset(data, d, sheight * swidth); // } /* * corners */ ylimit = ps->cgsize; if (ylimit > sheight / 2) ylimit = (sheight + 1) / 2; xlimit = ps->cgsize; if (xlimit > swidth / 2) xlimit = (swidth + 1) / 2; for (y = 0; y < ylimit; y++) { for (x = 0; x < xlimit; x++) { if (xlimit == ps->cgsize && ylimit == ps->cgsize) { d = ps->shadow_corner[opacity_int * (ps->cgsize + 1) * (ps->cgsize + 1) + y * (ps->cgsize + 1) + x]; } else { d = sum_gaussian(ps->gaussian_map, opacity, x - center, y - center, width, height); } data[y * swidth + x] = d; data[(sheight - y - 1) * swidth + x] = d; data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d; data[y * swidth + (swidth - x - 1)] = d; } } /* * top/bottom */ x_diff = swidth - (ps->cgsize * 2); if (x_diff > 0 && ylimit > 0) { for (y = 0; y < ylimit; y++) { if (ylimit == ps->cgsize) { d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + y]; } else { d = sum_gaussian(ps->gaussian_map, opacity, center, y - center, width, height); } memset(&data[y * swidth + ps->cgsize], d, x_diff); memset(&data[(sheight - y - 1) * swidth + ps->cgsize], d, x_diff); } } /* * sides */ for (x = 0; x < xlimit; x++) { if (xlimit == ps->cgsize) { d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + x]; } else { d = sum_gaussian(ps->gaussian_map, opacity, x - center, center, width, height); } for (y = ps->cgsize; y < sheight - ps->cgsize; y++) { data[y * swidth + x] = d; data[y * swidth + (swidth - x - 1)] = d; } } /* if (clear_shadow) { // Clear the region in the shadow that the window would cover based // on shadow_offset_{x,y} user provides int xstart = normalize_i_range(- (int) ps->o.shadow_offset_x, 0, swidth); int xrange = normalize_i_range(width - (int) ps->o.shadow_offset_x, 0, swidth) - xstart; int ystart = normalize_i_range(- (int) ps->o.shadow_offset_y, 0, sheight); int yend = normalize_i_range(height - (int) ps->o.shadow_offset_y, 0, sheight); int y; for (y = ystart; y < yend; y++) { memset(&data[y * swidth + xstart], 0, xrange); } } */ return ximage; } /** * Generate shadow Picture for a window. */ static bool win_build_shadow(session_t *ps, win *w, double opacity) { const int width = w->widthb; const int height = w->heightb; XImage *shadow_image = NULL; Pixmap shadow_pixmap = None, shadow_pixmap_argb = None; Picture shadow_picture = None, shadow_picture_argb = None; GC gc = None; shadow_image = make_shadow(ps, opacity, width, height); if (!shadow_image) return None; shadow_pixmap = XCreatePixmap(ps->dpy, ps->root, shadow_image->width, shadow_image->height, 8); shadow_pixmap_argb = XCreatePixmap(ps->dpy, ps->root, shadow_image->width, shadow_image->height, 32); if (!shadow_pixmap || !shadow_pixmap_argb) goto shadow_picture_err; shadow_picture = XRenderCreatePicture(ps->dpy, shadow_pixmap, XRenderFindStandardFormat(ps->dpy, PictStandardA8), 0, 0); shadow_picture_argb = XRenderCreatePicture(ps->dpy, shadow_pixmap_argb, XRenderFindStandardFormat(ps->dpy, PictStandardARGB32), 0, 0); if (!shadow_picture || !shadow_picture_argb) goto shadow_picture_err; gc = XCreateGC(ps->dpy, shadow_pixmap, 0, 0); if (!gc) goto shadow_picture_err; XPutImage(ps->dpy, shadow_pixmap, gc, shadow_image, 0, 0, 0, 0, shadow_image->width, shadow_image->height); XRenderComposite(ps->dpy, PictOpSrc, ps->cshadow_picture, shadow_picture, shadow_picture_argb, 0, 0, 0, 0, 0, 0, shadow_image->width, shadow_image->height); assert(!w->shadow_paint.pixmap); w->shadow_paint.pixmap = shadow_pixmap_argb; assert(!w->shadow_paint.pict); w->shadow_paint.pict = shadow_picture_argb; // Sync it once and only once xr_sync(ps, w->shadow_paint.pixmap, NULL); XFreeGC(ps->dpy, gc); XDestroyImage(shadow_image); XFreePixmap(ps->dpy, shadow_pixmap); XRenderFreePicture(ps->dpy, shadow_picture); return true; shadow_picture_err: if (shadow_image) XDestroyImage(shadow_image); if (shadow_pixmap) XFreePixmap(ps->dpy, shadow_pixmap); if (shadow_pixmap_argb) XFreePixmap(ps->dpy, shadow_pixmap_argb); if (shadow_picture) XRenderFreePicture(ps->dpy, shadow_picture); if (shadow_picture_argb) XRenderFreePicture(ps->dpy, shadow_picture_argb); if (gc) XFreeGC(ps->dpy, gc); return false; } /** * Generate a 1x1 Picture of a particular color. */ static Picture solid_picture(session_t *ps, bool argb, double a, double r, double g, double b) { Pixmap pixmap; Picture picture; XRenderPictureAttributes pa; XRenderColor c; pixmap = XCreatePixmap(ps->dpy, ps->root, 1, 1, argb ? 32 : 8); if (!pixmap) return None; pa.repeat = True; picture = XRenderCreatePicture(ps->dpy, pixmap, XRenderFindStandardFormat(ps->dpy, argb ? PictStandardARGB32 : PictStandardA8), CPRepeat, &pa); if (!picture) { XFreePixmap(ps->dpy, pixmap); return None; } c.alpha = a * 0xffff; c.red = r * 0xffff; c.green = g * 0xffff; c.blue = b * 0xffff; XRenderFillRectangle(ps->dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); XFreePixmap(ps->dpy, pixmap); return picture; } // === Error handling === static void discard_ignore(session_t *ps, unsigned long sequence) { while (ps->ignore_head) { if ((long) (sequence - ps->ignore_head->sequence) > 0) { ignore_t *next = ps->ignore_head->next; free(ps->ignore_head); ps->ignore_head = next; if (!ps->ignore_head) { ps->ignore_tail = &ps->ignore_head; } } else { break; } } } static void set_ignore(session_t *ps, unsigned long sequence) { if (ps->o.show_all_xerrors) return; ignore_t *i = malloc(sizeof(ignore_t)); if (!i) return; i->sequence = sequence; i->next = 0; *ps->ignore_tail = i; ps->ignore_tail = &i->next; } static int should_ignore(session_t *ps, unsigned long sequence) { discard_ignore(ps, sequence); return ps->ignore_head && ps->ignore_head->sequence == sequence; } // === Windows === /** * Get a specific attribute of a window. * * Returns a blank structure if the returned type and format does not * match the requested type and format. * * @param ps current session * @param w window * @param atom atom of attribute to fetch * @param length length to read * @param rtype atom of the requested type * @param rformat requested format * @return a winprop_t structure containing the attribute * and number of items. A blank one on failure. */ winprop_t wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, long length, Atom rtype, int rformat) { Atom type = None; int format = 0; unsigned long nitems = 0, after = 0; unsigned char *data = NULL; if (Success == XGetWindowProperty(ps->dpy, w, atom, offset, length, False, rtype, &type, &format, &nitems, &after, &data) && nitems && (AnyPropertyType == type || type == rtype) && (!rformat || format == rformat) && (8 == format || 16 == format || 32 == format)) { return (winprop_t) { .data.p8 = data, .nitems = nitems, .type = type, .format = format, }; } cxfree(data); return (winprop_t) { .data.p8 = NULL, .nitems = 0, .type = AnyPropertyType, .format = 0 }; } /** * Check if a window has rounded corners. */ static void win_rounded_corners(session_t *ps, win *w) { w->rounded_corners = false; if (!w->bounding_shaped) return; // Fetch its bounding region if (!w->border_size) w->border_size = border_size(ps, w, true); // Quit if border_size() returns None if (!w->border_size) return; // Determine the minimum width/height of a rectangle that could mark // a window as having rounded corners unsigned short minwidth = max_i(w->widthb * (1 - ROUNDED_PERCENT), w->widthb - ROUNDED_PIXELS); unsigned short minheight = max_i(w->heightb * (1 - ROUNDED_PERCENT), w->heightb - ROUNDED_PIXELS); // Get the rectangles in the bounding region int nrects = 0, i; XRectangle *rects = XFixesFetchRegion(ps->dpy, w->border_size, &nrects); if (!rects) return; // Look for a rectangle large enough for this window be considered // having rounded corners for (i = 0; i < nrects; ++i) if (rects[i].width >= minwidth && rects[i].height >= minheight) { w->rounded_corners = true; break; } cxfree(rects); } /** * Add a pattern to a condition linked list. */ static bool condlst_add(session_t *ps, c2_lptr_t **pcondlst, const char *pattern) { if (!pattern) return false; #ifdef CONFIG_C2 if (!c2_parse(ps, pcondlst, pattern)) exit(1); #else printf_errfq(1, "(): Condition support not compiled in."); #endif return true; } /** * Determine the event mask for a window. */ static long determine_evmask(session_t *ps, Window wid, win_evmode_t mode) { long evmask = NoEventMask; win *w = NULL; // Check if it's a mapped frame window if (WIN_EVMODE_FRAME == mode || ((w = find_win(ps, wid)) && IsViewable == w->a.map_state)) { evmask |= PropertyChangeMask; if (ps->o.track_focus && !ps->o.use_ewmh_active_win) evmask |= FocusChangeMask; } // Check if it's a mapped client window if (WIN_EVMODE_CLIENT == mode || ((w = find_toplevel(ps, wid)) && IsViewable == w->a.map_state)) { if (ps->o.frame_opacity || ps->o.track_wdata || ps->track_atom_lst || ps->o.detect_client_opacity) evmask |= PropertyChangeMask; } return evmask; } /** * Find out the WM frame of a client window by querying X. * * @param ps current session * @param wid window ID * @return struct _win object of the found window, NULL if not found */ static win * find_toplevel2(session_t *ps, Window wid) { win *w = NULL; // We traverse through its ancestors to find out the frame while (wid && wid != ps->root && !(w = find_win(ps, wid))) { Window troot; Window parent; Window *tchildren; unsigned tnchildren; // XQueryTree probably fails if you run compton when X is somehow // initializing (like add it in .xinitrc). In this case // just leave it alone. if (!XQueryTree(ps->dpy, wid, &troot, &parent, &tchildren, &tnchildren)) { parent = 0; break; } cxfree(tchildren); wid = parent; } return w; } /** * Recheck currently focused window and set its w->focused * to true. * * @param ps current session * @return struct _win of currently focused window, NULL if not found */ static win * recheck_focus(session_t *ps) { // Use EWMH _NET_ACTIVE_WINDOW if enabled if (ps->o.use_ewmh_active_win) { update_ewmh_active_win(ps); return ps->active_win; } // Determine the currently focused window so we can apply appropriate // opacity on it Window wid = 0; int revert_to; XGetInputFocus(ps->dpy, &wid, &revert_to); win *w = find_win_all(ps, wid); #ifdef DEBUG_EVENTS print_timestamp(ps); printf_dbgf("(): %#010lx (%#010lx \"%s\") focused.\n", wid, (w ? w->id: None), (w ? w->name: NULL)); #endif // And we set the focus state here if (w) { win_set_focused(ps, w, true); return w; } return NULL; } static bool get_root_tile(session_t *ps) { /* if (ps->o.paint_on_overlay) { return ps->root_picture; } */ assert(!ps->root_tile_paint.pixmap); ps->root_tile_fill = false; bool fill = false; Pixmap pixmap = None; // Get the values of background attributes for (int p = 0; background_props_str[p]; p++) { winprop_t prop = wid_get_prop(ps, ps->root, get_atom(ps, background_props_str[p]), 1L, XA_PIXMAP, 32); if (prop.nitems) { pixmap = *prop.data.p32; fill = false; free_winprop(&prop); break; } free_winprop(&prop); } // Make sure the pixmap we got is valid if (pixmap && !validate_pixmap(ps, pixmap)) pixmap = None; // Create a pixmap if there isn't any if (!pixmap) { pixmap = XCreatePixmap(ps->dpy, ps->root, 1, 1, ps->depth); fill = true; } // Create Picture { XRenderPictureAttributes pa = { .repeat = True, }; ps->root_tile_paint.pict = XRenderCreatePicture( ps->dpy, pixmap, XRenderFindVisualFormat(ps->dpy, ps->vis), CPRepeat, &pa); } // Fill pixmap if needed if (fill) { XRenderColor c; c.red = c.green = c.blue = 0x8080; c.alpha = 0xffff; XRenderFillRectangle(ps->dpy, PictOpSrc, ps->root_tile_paint.pict, &c, 0, 0, 1, 1); } ps->root_tile_fill = fill; ps->root_tile_paint.pixmap = pixmap; #ifdef CONFIG_VSYNC_OPENGL if (BKEND_GLX == ps->o.backend) return glx_bind_pixmap(ps, &ps->root_tile_paint.ptex, ps->root_tile_paint.pixmap, 0, 0, 0); #endif return true; } /** * Paint root window content. */ static void paint_root(session_t *ps, XserverRegion reg_paint) { if (!ps->root_tile_paint.pixmap) get_root_tile(ps); win_render(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint, NULL, ps->root_tile_paint.pict); } /** * Get a rectangular region a window occupies, excluding shadow. */ static XserverRegion win_get_region(session_t *ps, win *w, bool use_offset) { XRectangle r; r.x = (use_offset ? w->a.x: 0); r.y = (use_offset ? w->a.y: 0); r.width = w->widthb; r.height = w->heightb; return XFixesCreateRegion(ps->dpy, &r, 1); } /** * Get a rectangular region a window occupies, excluding frame and shadow. */ static XserverRegion win_get_region_noframe(session_t *ps, win *w, bool use_offset) { const margin_t extents = win_calc_frame_extents(ps, w); XRectangle r; r.x = (use_offset ? w->a.x: 0) + extents.left; r.y = (use_offset ? w->a.y: 0) + extents.top; r.width = max_i(w->a.width - extents.left - extents.right, 0); r.height = max_i(w->a.height - extents.top - extents.bottom, 0); if (r.width > 0 && r.height > 0) return XFixesCreateRegion(ps->dpy, &r, 1); else return XFixesCreateRegion(ps->dpy, NULL, 0); } /** * Get a rectangular region a window (and possibly its shadow) occupies. * * Note w->shadow and shadow geometry must be correct before calling this * function. */ static XserverRegion win_extents(session_t *ps, win *w) { XRectangle r; r.x = w->a.x; r.y = w->a.y; r.width = w->widthb; r.height = w->heightb; if (w->shadow) { XRectangle sr; sr.x = w->a.x + w->shadow_dx; sr.y = w->a.y + w->shadow_dy; sr.width = w->shadow_width; sr.height = w->shadow_height; if (sr.x < r.x) { r.width = (r.x + r.width) - sr.x; r.x = sr.x; } if (sr.y < r.y) { r.height = (r.y + r.height) - sr.y; r.y = sr.y; } if (sr.x + sr.width > r.x + r.width) { r.width = sr.x + sr.width - r.x; } if (sr.y + sr.height > r.y + r.height) { r.height = sr.y + sr.height - r.y; } } return XFixesCreateRegion(ps->dpy, &r, 1); } /** * Retrieve the bounding shape of a window. */ static XserverRegion border_size(session_t *ps, win *w, bool use_offset) { // Start with the window rectangular region XserverRegion fin = win_get_region(ps, w, use_offset); // Only request for a bounding region if the window is shaped if (w->bounding_shaped) { /* * if window doesn't exist anymore, this will generate an error * as well as not generate a region. Perhaps a better XFixes * architecture would be to have a request that copies instead * of creates, that way you'd just end up with an empty region * instead of an invalid XID. */ XserverRegion border = XFixesCreateRegionFromWindow( ps->dpy, w->id, WindowRegionBounding); if (!border) return fin; if (use_offset) { // Translate the region to the correct place XFixesTranslateRegion(ps->dpy, border, w->a.x + w->a.border_width, w->a.y + w->a.border_width); } // Intersect the bounding region we got with the window rectangle, to // make sure the bounding region is not bigger than the window // rectangle XFixesIntersectRegion(ps->dpy, fin, fin, border); XFixesDestroyRegion(ps->dpy, border); } return fin; } /** * Look for the client window of a particular window. */ static Window find_client_win(session_t *ps, Window w) { if (wid_has_prop(ps, w, ps->atom_client)) { return w; } Window *children; unsigned int nchildren; unsigned int i; Window ret = 0; if (!wid_get_children(ps, w, &children, &nchildren)) { return 0; } for (i = 0; i < nchildren; ++i) { if ((ret = find_client_win(ps, children[i]))) break; } cxfree(children); return ret; } /** * Retrieve frame extents from a window. */ static void get_frame_extents(session_t *ps, win *w, Window client) { cmemzero_one(&w->frame_extents); winprop_t prop = wid_get_prop(ps, client, ps->atom_frame_extents, 4L, XA_CARDINAL, 32); if (4 == prop.nitems) { const long * const extents = prop.data.p32; w->frame_extents.left = extents[0]; w->frame_extents.right = extents[1]; w->frame_extents.top = extents[2]; w->frame_extents.bottom = extents[3]; if (ps->o.frame_opacity) update_reg_ignore_expire(ps, w); } #ifdef DEBUG_FRAME printf_dbgf("(%#010lx): %d, %d, %d, %d\n", w->id, w->frame_extents.left, w->frame_extents.right, w->frame_extents.top, w->frame_extents.bottom); #endif free_winprop(&prop); } /** * Get alpha Picture for an opacity in double. */ static inline Picture get_alpha_pict_d(session_t *ps, double o) { assert((round(normalize_d(o) / ps->o.alpha_step)) <= round(1.0 / ps->o.alpha_step)); return ps->alpha_picts[(int) (round(normalize_d(o) / ps->o.alpha_step))]; } /** * Get alpha Picture for an opacity in * opacity_t. */ static inline Picture get_alpha_pict_o(session_t *ps, opacity_t o) { return get_alpha_pict_d(ps, (double) o / OPAQUE); } static win * paint_preprocess(session_t *ps, win *list) { win *t = NULL, *next = NULL; // Fading step calculation time_ms_t steps = 0L; if (ps->fade_time) { steps = ((get_time_ms() - ps->fade_time) + FADE_DELTA_TOLERANCE * ps->o.fade_delta) / ps->o.fade_delta; } // Reset fade_time if unset, or there appears to be a time disorder if (!ps->fade_time || steps < 0L) { ps->fade_time = get_time_ms(); steps = 0L; } ps->fade_time += steps * ps->o.fade_delta; XserverRegion last_reg_ignore = None; bool unredir_possible = false; // Trace whether it's the highest window to paint bool is_highest = true; for (win *w = list; w; w = next) { bool to_paint = true; const winmode_t mode_old = w->mode; // In case calling the fade callback function destroys this window next = w->next; opacity_t opacity_old = w->opacity; // Data expiration { // Remove built shadow if needed if (w->flags & WFLAG_SIZE_CHANGE) free_paint(ps, &w->shadow_paint); // Destroy reg_ignore on all windows if they should expire if (ps->reg_ignore_expire) free_region(ps, &w->reg_ignore); } // Restore flags from last paint if the window is being faded out if (IsUnmapped == w->a.map_state) { win_set_shadow(ps, w, w->shadow_last); w->fade = w->fade_last; win_set_invert_color(ps, w, w->invert_color_last); win_set_blur_background(ps, w, w->blur_background_last); } // Update window opacity target and dim state if asked if (WFLAG_OPCT_CHANGE & w->flags) { calc_opacity(ps, w); calc_dim(ps, w); } // Run fading run_fade(ps, w, steps); // Opacity will not change, from now on. // Give up if it's not damaged or invisible, or it's unmapped and its // pixmap is gone (for example due to a ConfigureNotify), or when it's // excluded if (!w->damaged || w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 || w->a.x >= ps->root_width || w->a.y >= ps->root_height || ((IsUnmapped == w->a.map_state || w->destroyed) && !w->paint.pixmap) || get_alpha_pict_o(ps, w->opacity) == ps->alpha_picts[0] || w->paint_excluded) to_paint = false; // to_paint will never change afterward // Determine mode as early as possible if (to_paint && (!w->to_paint || w->opacity != opacity_old)) win_determine_mode(ps, w); if (to_paint) { // Fetch bounding region if (!w->border_size) w->border_size = border_size(ps, w, true); // Fetch window extents if (!w->extents) w->extents = win_extents(ps, w); // Calculate frame_opacity { double frame_opacity_old = w->frame_opacity; if (ps->o.frame_opacity && 1.0 != ps->o.frame_opacity && win_has_frame(w)) w->frame_opacity = get_opacity_percent(w) * ps->o.frame_opacity; else w->frame_opacity = 0.0; // Destroy all reg_ignore above when frame opaque state changes on // SOLID mode if (w->to_paint && WMODE_SOLID == mode_old && (0.0 == frame_opacity_old) != (0.0 == w->frame_opacity)) ps->reg_ignore_expire = true; } // Calculate shadow opacity if (w->frame_opacity) w->shadow_opacity = ps->o.shadow_opacity * w->frame_opacity; else w->shadow_opacity = ps->o.shadow_opacity * get_opacity_percent(w); } // Add window to damaged area if its painting status changes // or opacity changes if (to_paint != w->to_paint || w->opacity != opacity_old) add_damage_win(ps, w); // Destroy all reg_ignore above when window mode changes if ((to_paint && WMODE_SOLID == w->mode) != (w->to_paint && WMODE_SOLID == mode_old)) ps->reg_ignore_expire = true; if (to_paint) { // Generate ignore region for painting to reduce GPU load if (ps->reg_ignore_expire || !w->to_paint) { free_region(ps, &w->reg_ignore); // If the window is solid, we add the window region to the // ignored region if (win_is_solid(ps, w)) { if (!w->frame_opacity) { if (w->border_size) w->reg_ignore = copy_region(ps, w->border_size); else w->reg_ignore = win_get_region(ps, w, true); } else { w->reg_ignore = win_get_region_noframe(ps, w, true); if (w->border_size) XFixesIntersectRegion(ps->dpy, w->reg_ignore, w->reg_ignore, w->border_size); } if (last_reg_ignore) XFixesUnionRegion(ps->dpy, w->reg_ignore, w->reg_ignore, last_reg_ignore); } // Otherwise we copy the last region over else if (last_reg_ignore) w->reg_ignore = copy_region(ps, last_reg_ignore); else w->reg_ignore = None; } last_reg_ignore = w->reg_ignore; // (Un)redirect screen // We could definitely unredirect the screen when there's no window to // paint, but this is typically unnecessary, may cause flickering when // fading is enabled, and could create inconsistency when the wallpaper // is not correctly set. if (ps->o.unredir_if_possible && is_highest && to_paint) { is_highest = false; if (win_is_solid(ps, w) && (!w->frame_opacity || !win_has_frame(w)) && win_is_fullscreen(ps, w) && !w->unredir_if_possible_excluded) unredir_possible = true; } // Reset flags w->flags = 0; } // Avoid setting w->to_paint if w is to be freed bool destroyed = (w->opacity_tgt == w->opacity && w->destroyed); if (to_paint) { w->prev_trans = t; t = w; } else { assert(w->destroyed == (w->fade_callback == destroy_callback)); check_fade_fin(ps, w); } if (!destroyed) { w->to_paint = to_paint; if (w->to_paint) { // Save flags w->shadow_last = w->shadow; w->fade_last = w->fade; w->invert_color_last = w->invert_color; w->blur_background_last = w->blur_background; } } } // If possible, unredirect all windows and stop painting if (UNSET != ps->o.redirected_force) unredir_possible = !ps->o.redirected_force; // If there's no window to paint, and the screen isn't redirected, // don't redirect it. if (ps->o.unredir_if_possible && is_highest && !ps->redirected) unredir_possible = true; if (unredir_possible) { if (ps->redirected) { if (!ps->o.unredir_if_possible_delay || ps->tmout_unredir_hit) redir_stop(ps); else if (!ps->tmout_unredir->enabled) { timeout_reset(ps, ps->tmout_unredir); ps->tmout_unredir->enabled = true; } } } else { ps->tmout_unredir->enabled = false; redir_start(ps); } return t; } /** * Paint the shadow of a window. */ static inline void win_paint_shadow(session_t *ps, win *w, XserverRegion reg_paint, const reg_data_t *pcache_reg) { // Bind shadow pixmap to GLX texture if needed paint_bind_tex(ps, &w->shadow_paint, 0, 0, 32, false); if (!paint_isvalid(ps, &w->shadow_paint)) { printf_errf("(%#010lx): Missing painting data. This is a bad sign.", w->id); return; } render(ps, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, w->shadow_width, w->shadow_height, w->shadow_opacity, true, false, w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, pcache_reg, NULL); } /** * Create an picture. */ static inline Picture xr_build_picture(session_t *ps, int wid, int hei, XRenderPictFormat *pictfmt) { if (!pictfmt) pictfmt = XRenderFindVisualFormat(ps->dpy, ps->vis); int depth = pictfmt->depth; Pixmap tmp_pixmap = XCreatePixmap(ps->dpy, ps->root, wid, hei, depth); if (!tmp_pixmap) return None; Picture tmp_picture = XRenderCreatePicture(ps->dpy, tmp_pixmap, pictfmt, 0, 0); free_pixmap(ps, &tmp_pixmap); return tmp_picture; } /** * @brief Blur an area on a buffer. * * @param ps current session * @param tgt_buffer a buffer as both source and destination * @param x x pos * @param y y pos * @param wid width * @param hei height * @param blur_kerns blur kernels, ending with a NULL, guaranteed to have at * least one kernel * @param reg_clip a clipping region to be applied on intermediate buffers * * @return true if successful, false otherwise */ static bool xr_blur_dst(session_t *ps, Picture tgt_buffer, int x, int y, int wid, int hei, XFixed **blur_kerns, XserverRegion reg_clip) { assert(blur_kerns[0]); // Directly copying from tgt_buffer to it does not work, so we create a // Picture in the middle. Picture tmp_picture = xr_build_picture(ps, wid, hei, NULL); if (!tmp_picture) { printf_errf("(): Failed to build intermediate Picture."); return false; } if (reg_clip && tmp_picture) XFixesSetPictureClipRegion(ps->dpy, tmp_picture, reg_clip, 0, 0); Picture src_pict = tgt_buffer, dst_pict = tmp_picture; for (int i = 0; blur_kerns[i]; ++i) { assert(i < MAX_BLUR_PASS - 1); XFixed *convolution_blur = blur_kerns[i]; int kwid = XFixedToDouble(convolution_blur[0]), khei = XFixedToDouble(convolution_blur[1]); bool rd_from_tgt = (tgt_buffer == src_pict); // Copy from source picture to destination. The filter must // be applied on source picture, to get the nearby pixels outside the // window. XRenderSetPictureFilter(ps->dpy, src_pict, XRFILTER_CONVOLUTION, convolution_blur, kwid * khei + 2); XRenderComposite(ps->dpy, PictOpSrc, src_pict, None, dst_pict, (rd_from_tgt ? x: 0), (rd_from_tgt ? y: 0), 0, 0, (rd_from_tgt ? 0: x), (rd_from_tgt ? 0: y), wid, hei); xrfilter_reset(ps, src_pict); { XserverRegion tmp = src_pict; src_pict = dst_pict; dst_pict = tmp; } } if (src_pict != tgt_buffer) XRenderComposite(ps->dpy, PictOpSrc, src_pict, None, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); free_picture(ps, &tmp_picture); return true; } /* * WORK-IN-PROGRESS! static void xr_take_screenshot(session_t *ps) { XImage *img = XGetImage(ps->dpy, get_tgt_window(ps), 0, 0, ps->root_width, ps->root_height, AllPlanes, XYPixmap); if (!img) { printf_errf("(): Failed to get XImage."); return NULL; } assert(0 == img->xoffset); } */ /** * Blur the background of a window. */ static inline void win_blur_background(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint, const reg_data_t *pcache_reg) { const int x = w->a.x; const int y = w->a.y; const int wid = w->widthb; const int hei = w->heightb; double factor_center = 1.0; // Adjust blur strength according to window opacity, to make it appear // better during fading if (!ps->o.blur_background_fixed) { double pct = 1.0 - get_opacity_percent(w) * (1.0 - 1.0 / 9.0); factor_center = pct * 8.0 / (1.1 - pct); } switch (ps->o.backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: { // Normalize blur kernels for (int i = 0; i < MAX_BLUR_PASS; ++i) { XFixed *kern_src = ps->o.blur_kerns[i]; XFixed *kern_dst = ps->blur_kerns_cache[i]; assert(i < MAX_BLUR_PASS); if (!kern_src) { assert(!kern_dst); break; } assert(!kern_dst || (kern_src[0] == kern_dst[0] && kern_src[1] == kern_dst[1])); // Skip for fixed factor_center if the cache exists already if (ps->o.blur_background_fixed && kern_dst) continue; int kwid = XFixedToDouble(kern_src[0]), khei = XFixedToDouble(kern_src[1]); // Allocate cache space if needed if (!kern_dst) { kern_dst = malloc((kwid * khei + 2) * sizeof(XFixed)); if (!kern_dst) { printf_errf("(): Failed to allocate memory for blur kernel."); return; } ps->blur_kerns_cache[i] = kern_dst; } // Modify the factor of the center pixel kern_src[2 + (khei / 2) * kwid + kwid / 2] = XDoubleToFixed(factor_center); // Copy over memcpy(kern_dst, kern_src, (kwid * khei + 2) * sizeof(XFixed)); normalize_conv_kern(kwid, khei, kern_dst + 2); } // Minimize the region we try to blur, if the window itself is not // opaque, only the frame is. XserverRegion reg_noframe = None; if (win_is_solid(ps, w)) { XserverRegion reg_all = border_size(ps, w, false); reg_noframe = win_get_region_noframe(ps, w, false); XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); free_region(ps, ®_all); } xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache, reg_noframe); free_region(ps, ®_noframe); } break; #ifdef CONFIG_VSYNC_OPENGL_GLSL case BKEND_GLX: // TODO: Handle frame opacity glx_blur_dst(ps, x, y, wid, hei, ps->psglx->z - 0.5, factor_center, reg_paint, pcache_reg, &w->glx_blur_cache); break; #endif default: assert(0); } } static void render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity, bool argb, bool neg, Picture pict, glx_texture_t *ptex, XserverRegion reg_paint, const reg_data_t *pcache_reg #ifdef CONFIG_VSYNC_OPENGL_GLSL , const glx_prog_main_t *pprogram #endif ) { switch (ps->o.backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: { Picture alpha_pict = get_alpha_pict_d(ps, opacity); if (alpha_pict != ps->alpha_picts[0]) { int op = ((!argb && !alpha_pict) ? PictOpSrc: PictOpOver); XRenderComposite(ps->dpy, op, pict, alpha_pict, ps->tgt_buffer.pict, x, y, 0, 0, dx, dy, wid, hei); } break; } #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: glx_render(ps, ptex, x, y, dx, dy, wid, hei, ps->psglx->z, opacity, argb, neg, reg_paint, pcache_reg, pprogram); ps->psglx->z += 1; break; #endif default: assert(0); } } /** * Paint a window itself and dim it if asked. */ static inline void win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, const reg_data_t *pcache_reg) { glx_mark(ps, w->id, true); // Fetch Pixmap if (!w->paint.pixmap && ps->has_name_pixmap) { set_ignore_next(ps); w->paint.pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id); if (w->paint.pixmap) free_fence(ps, &w->fence); } Drawable draw = w->paint.pixmap; if (!draw) draw = w->id; // XRender: Build picture if (bkend_use_xrender(ps) && !w->paint.pict) { { XRenderPictureAttributes pa = { .subwindow_mode = IncludeInferiors, }; w->paint.pict = XRenderCreatePicture(ps->dpy, draw, w->pictfmt, CPSubwindowMode, &pa); } } if (IsViewable == w->a.map_state) xr_sync(ps, draw, &w->fence); // GLX: Build texture // Let glx_bind_pixmap() determine pixmap size, because if the user // is resizing windows, the width and height we get may not be up-to-date, // causing the jittering issue M4he reported in #7. if (!paint_bind_tex(ps, &w->paint, 0, 0, 0, (!ps->o.glx_no_rebind_pixmap && w->pixmap_damaged))) { printf_errf("(%#010lx): Failed to bind texture. Expect troubles.", w->id); } w->pixmap_damaged = false; if (!paint_isvalid(ps, &w->paint)) { printf_errf("(%#010lx): Missing painting data. This is a bad sign.", w->id); return; } const int x = w->a.x; const int y = w->a.y; const int wid = w->widthb; const int hei = w->heightb; Picture pict = w->paint.pict; // Invert window color, if required if (bkend_use_xrender(ps) && w->invert_color) { Picture newpict = xr_build_picture(ps, wid, hei, w->pictfmt); if (newpict) { // Apply clipping region to save some CPU if (reg_paint) { XserverRegion reg = copy_region(ps, reg_paint); XFixesTranslateRegion(ps->dpy, reg, -x, -y); XFixesSetPictureClipRegion(ps->dpy, newpict, 0, 0, reg); free_region(ps, ®); } XRenderComposite(ps->dpy, PictOpSrc, pict, None, newpict, 0, 0, 0, 0, 0, 0, wid, hei); XRenderComposite(ps->dpy, PictOpDifference, ps->white_picture, None, newpict, 0, 0, 0, 0, 0, 0, wid, hei); // We use an extra PictOpInReverse operation to get correct pixel // alpha. There could be a better solution. if (WMODE_ARGB == w->mode) XRenderComposite(ps->dpy, PictOpInReverse, pict, None, newpict, 0, 0, 0, 0, 0, 0, wid, hei); pict = newpict; } } const double dopacity = get_opacity_percent(w); if (!w->frame_opacity) { win_render(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pcache_reg, pict); } else { // Painting parameters const margin_t extents = win_calc_frame_extents(ps, w); const int t = extents.top; const int l = extents.left; const int b = extents.bottom; const int r = extents.right; #define COMP_BDR(cx, cy, cwid, chei) \ win_render(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity, \ reg_paint, pcache_reg, pict) // The following complicated logic is required because some broken // window managers (I'm talking about you, Openbox!) that makes // top_width + bottom_width > height in some cases. // top int phei = min_i(t, hei); if (phei > 0) COMP_BDR(0, 0, wid, phei); if (hei > t) { phei = min_i(hei - t, b); // bottom if (phei > 0) COMP_BDR(0, hei - phei, wid, phei); phei = hei - t - phei; if (phei > 0) { int pwid = min_i(l, wid); // left if (pwid > 0) COMP_BDR(0, t, pwid, phei); if (wid > l) { pwid = min_i(wid - l, r); // right if (pwid > 0) COMP_BDR(wid - pwid, t, pwid, phei); pwid = wid - l - pwid; if (pwid > 0) { // body win_render(ps, w, l, t, pwid, phei, dopacity, reg_paint, pcache_reg, pict); } } } } } #undef COMP_BDR if (pict != w->paint.pict) free_picture(ps, &pict); // Dimming the window if needed if (w->dim) { double dim_opacity = ps->o.inactive_dim; if (!ps->o.inactive_dim_fixed) dim_opacity *= get_opacity_percent(w); switch (ps->o.backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: { unsigned short cval = 0xffff * dim_opacity; // Premultiply color XRenderColor color = { .red = 0, .green = 0, .blue = 0, .alpha = cval, }; XRectangle rect = { .x = x, .y = y, .width = wid, .height = hei, }; XRenderFillRectangles(ps->dpy, PictOpOver, ps->tgt_buffer.pict, &color, &rect, 1); } break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: glx_dim_dst(ps, x, y, wid, hei, ps->psglx->z - 0.7, dim_opacity, reg_paint, pcache_reg); break; #endif } } glx_mark(ps, w->id, false); } /** * Rebuild cached screen_reg. */ static void rebuild_screen_reg(session_t *ps) { if (ps->screen_reg) XFixesDestroyRegion(ps->dpy, ps->screen_reg); ps->screen_reg = get_screen_region(ps); } /** * Rebuild shadow_exclude_reg. */ static void rebuild_shadow_exclude_reg(session_t *ps) { free_region(ps, &ps->shadow_exclude_reg); XRectangle rect = geom_to_rect(ps, &ps->o.shadow_exclude_reg_geom, NULL); ps->shadow_exclude_reg = rect_to_reg(ps, &rect); } static void paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t) { if (!region_real) region_real = region; #ifdef DEBUG_REPAINT static struct timespec last_paint = { 0 }; #endif XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None; #ifdef CONFIG_VSYNC_OPENGL if (bkend_use_glx(ps)) { glx_paint_pre(ps, ®ion); } #endif if (!region) { region_real = region = get_screen_region(ps); } else { // Remove the damaged area out of screen XFixesIntersectRegion(ps->dpy, region, region, ps->screen_reg); } #ifdef MONITOR_REPAINT // Note: MONITOR_REPAINT cannot work with DBE right now. // Picture old_tgt_buffer = ps->tgt_buffer.pict; ps->tgt_buffer.pict = ps->tgt_picture; #else if (!paint_isvalid(ps, &ps->tgt_buffer)) { // DBE painting mode: Directly paint to a Picture of the back buffer if (BKEND_XRENDER == ps->o.backend && ps->o.dbe) { ps->tgt_buffer.pict = XRenderCreatePicture(ps->dpy, ps->root_dbe, XRenderFindVisualFormat(ps->dpy, ps->vis), 0, 0); } // No-DBE painting mode: Paint to an intermediate Picture then paint // the Picture to root window else { if (!ps->tgt_buffer.pixmap) { free_paint(ps, &ps->tgt_buffer); ps->tgt_buffer.pixmap = XCreatePixmap(ps->dpy, ps->root, ps->root_width, ps->root_height, ps->depth); } if (BKEND_GLX != ps->o.backend) ps->tgt_buffer.pict = XRenderCreatePicture(ps->dpy, ps->tgt_buffer.pixmap, XRenderFindVisualFormat(ps->dpy, ps->vis), 0, 0); } } #endif if (BKEND_XRENDER == ps->o.backend) XFixesSetPictureClipRegion(ps->dpy, ps->tgt_picture, 0, 0, region_real); #ifdef MONITOR_REPAINT switch (ps->o.backend) { case BKEND_XRENDER: XRenderComposite(ps->dpy, PictOpSrc, ps->black_picture, None, ps->tgt_picture, 0, 0, 0, 0, 0, 0, ps->root_width, ps->root_height); break; case BKEND_GLX: case BKEND_XR_GLX_HYBRID: glClearColor(0.0f, 0.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); break; } #endif if (t && t->reg_ignore) { // Calculate the region upon which the root window is to be painted // based on the ignore region of the lowest window, if available reg_paint = reg_tmp = XFixesCreateRegion(ps->dpy, NULL, 0); XFixesSubtractRegion(ps->dpy, reg_paint, region, t->reg_ignore); } else { reg_paint = region; } set_tgt_clip(ps, reg_paint, NULL); paint_root(ps, reg_paint); // Create temporary regions for use during painting if (!reg_tmp) reg_tmp = XFixesCreateRegion(ps->dpy, NULL, 0); reg_tmp2 = XFixesCreateRegion(ps->dpy, NULL, 0); for (win *w = t; w; w = w->prev_trans) { // Painting shadow if (w->shadow) { // Lazy shadow building if (!w->shadow_paint.pixmap) win_build_shadow(ps, w, 1); // Shadow is to be painted based on the ignore region of current // window if (w->reg_ignore) { if (w == t) { // If it's the first cycle and reg_tmp2 is not ready, calculate // the paint region here reg_paint = reg_tmp; XFixesSubtractRegion(ps->dpy, reg_paint, region, w->reg_ignore); } else { // Otherwise, used the cached region during last cycle reg_paint = reg_tmp2; } XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, w->extents); } else { reg_paint = reg_tmp; XFixesIntersectRegion(ps->dpy, reg_paint, region, w->extents); } if (ps->shadow_exclude_reg) XFixesSubtractRegion(ps->dpy, reg_paint, reg_paint, ps->shadow_exclude_reg); // Might be worthwhile to crop the region to shadow border { XRectangle rec_shadow_border = { .x = w->a.x + w->shadow_dx, .y = w->a.y + w->shadow_dy, .width = w->shadow_width, .height = w->shadow_height }; XserverRegion reg_shadow = XFixesCreateRegion(ps->dpy, &rec_shadow_border, 1); XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, reg_shadow); free_region(ps, ®_shadow); } // Clear the shadow here instead of in make_shadow() for saving GPU // power and handling shaped windows if (ps->o.clear_shadow && w->border_size) XFixesSubtractRegion(ps->dpy, reg_paint, reg_paint, w->border_size); #ifdef CONFIG_XINERAMA if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0) XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, ps->xinerama_scr_regs[w->xinerama_scr]); #endif // Detect if the region is empty before painting { reg_data_t cache_reg = REG_DATA_INIT; if (region == reg_paint || !is_region_empty(ps, reg_paint, &cache_reg)) { set_tgt_clip(ps, reg_paint, &cache_reg); win_paint_shadow(ps, w, reg_paint, &cache_reg); } free_reg_data(&cache_reg); } } // Calculate the region based on the reg_ignore of the next (higher) // window and the bounding region reg_paint = reg_tmp; if (w->prev_trans && w->prev_trans->reg_ignore) { XFixesSubtractRegion(ps->dpy, reg_paint, region, w->prev_trans->reg_ignore); // Copy the subtracted region to be used for shadow painting in next // cycle XFixesCopyRegion(ps->dpy, reg_tmp2, reg_paint); if (w->border_size) XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, w->border_size); } else { if (w->border_size) XFixesIntersectRegion(ps->dpy, reg_paint, region, w->border_size); else reg_paint = region; } { reg_data_t cache_reg = REG_DATA_INIT; if (!is_region_empty(ps, reg_paint, &cache_reg)) { set_tgt_clip(ps, reg_paint, &cache_reg); // Blur window background if (w->blur_background && (!win_is_solid(ps, w) || (ps->o.blur_background_frame && w->frame_opacity))) { win_blur_background(ps, w, ps->tgt_buffer.pict, reg_paint, &cache_reg); } // Painting the window win_paint_win(ps, w, reg_paint, &cache_reg); } free_reg_data(&cache_reg); } } // Free up all temporary regions XFixesDestroyRegion(ps->dpy, reg_tmp); XFixesDestroyRegion(ps->dpy, reg_tmp2); // Do this as early as possible if (!ps->o.dbe) set_tgt_clip(ps, None, NULL); if (ps->o.vsync) { // Make sure all previous requests are processed to achieve best // effect XSync(ps->dpy, False); #ifdef CONFIG_VSYNC_OPENGL if (glx_has_context(ps)) { if (ps->o.vsync_use_glfinish) glFinish(); else glFlush(); glXWaitX(); } #endif } // Wait for VBlank. We could do it aggressively (send the painting // request and XFlush() on VBlank) or conservatively (send the request // only on VBlank). if (!ps->o.vsync_aggressive) vsync_wait(ps); switch (ps->o.backend) { case BKEND_XRENDER: // DBE painting mode, only need to swap the buffer if (ps->o.dbe) { XdbeSwapInfo swap_info = { .swap_window = get_tgt_window(ps), // Is it safe to use XdbeUndefined? .swap_action = XdbeCopied }; XdbeSwapBuffers(ps->dpy, &swap_info, 1); } // No-DBE painting mode else if (ps->tgt_buffer.pict != ps->tgt_picture) { XRenderComposite( ps->dpy, PictOpSrc, ps->tgt_buffer.pict, None, ps->tgt_picture, 0, 0, 0, 0, 0, 0, ps->root_width, ps->root_height); } break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_XR_GLX_HYBRID: XSync(ps->dpy, False); if (ps->o.vsync_use_glfinish) glFinish(); else glFlush(); glXWaitX(); assert(ps->tgt_buffer.pixmap); xr_sync(ps, ps->tgt_buffer.pixmap, &ps->tgt_buffer_fence); paint_bind_tex_real(ps, &ps->tgt_buffer, ps->root_width, ps->root_height, ps->depth, !ps->o.glx_no_rebind_pixmap); // See #163 xr_sync(ps, ps->tgt_buffer.pixmap, &ps->tgt_buffer_fence); if (ps->o.vsync_use_glfinish) glFinish(); else glFlush(); glXWaitX(); glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width, ps->root_height, 0, 1.0, false, false, region_real, NULL, NULL); // No break here! case BKEND_GLX: if (ps->o.glx_use_copysubbuffermesa) glx_swap_copysubbuffermesa(ps, region_real); else glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break; #endif default: assert(0); } glx_mark_frame(ps); if (ps->o.vsync_aggressive) vsync_wait(ps); XFlush(ps->dpy); #ifdef CONFIG_VSYNC_OPENGL if (glx_has_context(ps)) { glFlush(); glXWaitX(); } #endif XFixesDestroyRegion(ps->dpy, region); #ifdef DEBUG_REPAINT print_timestamp(ps); struct timespec now = get_time_timespec(); struct timespec diff = { 0 }; timespec_subtract(&diff, &now, &last_paint); printf("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec); last_paint = now; printf("paint:"); for (win *w = t; w; w = w->prev_trans) printf(" %#010lx", w->id); putchar('\n'); fflush(stdout); #endif // Check if fading is finished on all painted windows { win *pprev = NULL; for (win *w = t; w; w = pprev) { pprev = w->prev_trans; check_fade_fin(ps, w); } } } static void add_damage(session_t *ps, XserverRegion damage) { // Ignore damage when screen isn't redirected if (!ps->redirected) free_region(ps, &damage); if (!damage) return; if (ps->all_damage) { XFixesUnionRegion(ps->dpy, ps->all_damage, ps->all_damage, damage); XFixesDestroyRegion(ps->dpy, damage); } else { ps->all_damage = damage; } } static void repair_win(session_t *ps, win *w) { if (IsViewable != w->a.map_state) return; XserverRegion parts; if (!w->damaged) { parts = win_extents(ps, w); set_ignore_next(ps); XDamageSubtract(ps->dpy, w->damage, None, None); } else { parts = XFixesCreateRegion(ps->dpy, 0, 0); set_ignore_next(ps); XDamageSubtract(ps->dpy, w->damage, None, parts); XFixesTranslateRegion(ps->dpy, parts, w->a.x + w->a.border_width, w->a.y + w->a.border_width); } w->damaged = true; w->pixmap_damaged = true; // Why care about damage when screen is unredirected? // We will force full-screen repaint on redirection. if (!ps->redirected) { free_region(ps, &parts); return; } // Remove the part in the damage area that could be ignored if (!ps->reg_ignore_expire && w->prev_trans && w->prev_trans->reg_ignore) XFixesSubtractRegion(ps->dpy, parts, parts, w->prev_trans->reg_ignore); add_damage(ps, parts); } static wintype_t wid_get_prop_wintype(session_t *ps, Window wid) { set_ignore_next(ps); winprop_t prop = wid_get_prop(ps, wid, ps->atom_win_type, 32L, XA_ATOM, 32); for (unsigned i = 0; i < prop.nitems; ++i) { for (wintype_t j = 1; j < NUM_WINTYPES; ++j) { if (ps->atoms_wintypes[j] == (Atom) prop.data.p32[i]) { free_winprop(&prop); return j; } } } free_winprop(&prop); return WINTYPE_UNKNOWN; } static void map_win(session_t *ps, Window id) { // Unmap overlay window if it got mapped but we are currently not // in redirected state. if (ps->overlay && id == ps->overlay && !ps->redirected) { XUnmapWindow(ps->dpy, ps->overlay); XFlush(ps->dpy); } win *w = find_win(ps, id); #ifdef DEBUG_EVENTS printf_dbgf("(%#010lx \"%s\"): %p\n", id, (w ? w->name: NULL), w); #endif // Don't care about window mapping if it's an InputOnly window // Try avoiding mapping a window twice if (!w || InputOnly == w->a.class || IsViewable == w->a.map_state) return; assert(!win_is_focused_real(ps, w)); w->a.map_state = IsViewable; cxinerama_win_upd_scr(ps, w); // Call XSelectInput() before reading properties so that no property // changes are lost XSelectInput(ps->dpy, id, determine_evmask(ps, id, WIN_EVMODE_FRAME)); // Notify compton when the shape of a window changes if (ps->shape_exists) { XShapeSelectInput(ps->dpy, id, ShapeNotifyMask); } // Make sure the XSelectInput() requests are sent XFlush(ps->dpy); // Update window mode here to check for ARGB windows win_determine_mode(ps, w); // Detect client window here instead of in add_win() as the client // window should have been prepared at this point if (!w->client_win) { win_recheck_client(ps, w); } else { // Re-mark client window here win_mark_client(ps, w, w->client_win); } assert(w->client_win); #ifdef DEBUG_WINTYPE printf_dbgf("(%#010lx): type %s\n", w->id, WINTYPES[w->window_type]); #endif // Detect if the window is shaped or has rounded corners win_update_shape_raw(ps, w); // FocusIn/Out may be ignored when the window is unmapped, so we must // recheck focus here if (ps->o.track_focus) recheck_focus(ps); // Update window focus state win_update_focused(ps, w); // Update opacity and dim state win_update_opacity_prop(ps, w); w->flags |= WFLAG_OPCT_CHANGE; // Check for _COMPTON_SHADOW if (ps->o.respect_prop_shadow) win_update_prop_shadow_raw(ps, w); // Many things above could affect shadow win_determine_shadow(ps, w); // Set fading state w->in_openclose = true; set_fade_callback(ps, w, finish_map_win, true); win_determine_fade(ps, w); win_determine_blur_background(ps, w); w->damaged = false; /* if any configure events happened while the window was unmapped, then configure the window to its correct place */ if (w->need_configure) { configure_win(ps, &w->queue_configure); } #ifdef CONFIG_DBUS // Send D-Bus signal if (ps->o.dbus) { cdbus_ev_win_mapped(ps, w); } #endif } static void finish_map_win(session_t *ps, win *w) { w->in_openclose = false; if (ps->o.no_fading_openclose) { win_determine_fade(ps, w); } } static void finish_unmap_win(session_t *ps, win *w) { w->damaged = false; w->in_openclose = false; update_reg_ignore_expire(ps, w); if (w->extents != None) { /* destroys region */ add_damage(ps, w->extents); w->extents = None; } free_wpaint(ps, w); free_region(ps, &w->border_size); free_paint(ps, &w->shadow_paint); } static void unmap_callback(session_t *ps, win *w) { finish_unmap_win(ps, w); } static void unmap_win(session_t *ps, win *w) { if (!w || IsUnmapped == w->a.map_state) return; // One last synchronization if (w->paint.pixmap) xr_sync(ps, w->paint.pixmap, &w->fence); free_fence(ps, &w->fence); // Set focus out win_set_focused(ps, w, false); w->a.map_state = IsUnmapped; // Fading out w->flags |= WFLAG_OPCT_CHANGE; set_fade_callback(ps, w, unmap_callback, false); w->in_openclose = true; win_determine_fade(ps, w); // Validate pixmap if we have to do fading if (w->fade) win_validate_pixmap(ps, w); // don't care about properties anymore win_ev_stop(ps, w); #ifdef CONFIG_DBUS // Send D-Bus signal if (ps->o.dbus) { cdbus_ev_win_unmapped(ps, w); } #endif } static opacity_t wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def) { opacity_t val = def; winprop_t prop = wid_get_prop(ps, wid, ps->atom_opacity, 1L, XA_CARDINAL, 32); if (prop.nitems) val = *prop.data.p32; free_winprop(&prop); return val; } static double get_opacity_percent(win *w) { return ((double) w->opacity) / OPAQUE; } static void win_determine_mode(session_t *ps, win *w) { winmode_t mode = WMODE_SOLID; if (w->pictfmt && w->pictfmt->type == PictTypeDirect && w->pictfmt->direct.alphaMask) { mode = WMODE_ARGB; } else if (w->opacity != OPAQUE) { mode = WMODE_TRANS; } else { mode = WMODE_SOLID; } w->mode = mode; } /** * Calculate and set the opacity of a window. * * If window is inactive and inactive_opacity_override is set, the * priority is: (Simulates the old behavior) * * inactive_opacity > _NET_WM_WINDOW_OPACITY (if not opaque) * > window type default opacity * * Otherwise: * * _NET_WM_WINDOW_OPACITY (if not opaque) * > window type default opacity (if not opaque) * > inactive_opacity * * @param ps current session * @param w struct _win object representing the window */ static void calc_opacity(session_t *ps, win *w) { opacity_t opacity = OPAQUE; if (w->destroyed || IsViewable != w->a.map_state) opacity = 0; else { // Try obeying opacity property and window type opacity firstly if (OPAQUE == (opacity = w->opacity_prop) && OPAQUE == (opacity = w->opacity_prop_client)) { opacity = ps->o.wintype_opacity[w->window_type] * OPAQUE; } // Respect inactive_opacity in some cases if (ps->o.inactive_opacity && false == w->focused && (OPAQUE == opacity || ps->o.inactive_opacity_override)) { opacity = ps->o.inactive_opacity; } // Respect active_opacity only when the window is physically focused if (OPAQUE == opacity && ps->o.active_opacity && win_is_focused_real(ps, w)) opacity = ps->o.active_opacity; } w->opacity_tgt = opacity; } /** * Determine whether a window is to be dimmed. */ static void calc_dim(session_t *ps, win *w) { bool dim; // Make sure we do nothing if the window is unmapped / destroyed if (w->destroyed || IsViewable != w->a.map_state) return; if (ps->o.inactive_dim && !(w->focused)) { dim = true; } else { dim = false; } if (dim != w->dim) { w->dim = dim; add_damage_win(ps, w); } } /** * Determine if a window should fade on opacity change. */ static void win_determine_fade(session_t *ps, win *w) { // To prevent it from being overwritten by last-paint value if the window is // unmapped on next frame, write w->fade_last as well if (UNSET != w->fade_force) w->fade_last = w->fade = w->fade_force; else if (ps->o.no_fading_openclose && w->in_openclose) w->fade_last = w->fade = false; else if (ps->o.no_fading_destroyed_argb && w->destroyed && WMODE_ARGB == w->mode && w->client_win && w->client_win != w->id) { w->fade_last = w->fade = false; } // Ignore other possible causes of fading state changes after window // gets unmapped else if (IsViewable != w->a.map_state) { } else if (win_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst)) w->fade = false; else w->fade = ps->o.wintype_fade[w->window_type]; } /** * Update window-shape. */ static void win_update_shape_raw(session_t *ps, win *w) { if (ps->shape_exists) { w->bounding_shaped = wid_bounding_shaped(ps, w->id); if (w->bounding_shaped && ps->o.detect_rounded_corners) win_rounded_corners(ps, w); } } /** * Update window-shape related information. */ static void win_update_shape(session_t *ps, win *w) { if (ps->shape_exists) { // bool bounding_shaped_old = w->bounding_shaped; win_update_shape_raw(ps, w); win_on_factor_change(ps, w); /* // If clear_shadow state on the window possibly changed, destroy the old // shadow_pict if (ps->o.clear_shadow && w->bounding_shaped != bounding_shaped_old) free_paint(ps, &w->shadow_paint); */ } } /** * Reread _COMPTON_SHADOW property from a window. * * The property must be set on the outermost window, usually the WM frame. */ static void win_update_prop_shadow_raw(session_t *ps, win *w) { winprop_t prop = wid_get_prop(ps, w->id, ps->atom_compton_shadow, 1, XA_CARDINAL, 32); if (!prop.nitems) { w->prop_shadow = -1; } else { w->prop_shadow = *prop.data.p32; } free_winprop(&prop); } /** * Reread _COMPTON_SHADOW property from a window and update related * things. */ static void win_update_prop_shadow(session_t *ps, win *w) { long attr_shadow_old = w->prop_shadow; win_update_prop_shadow_raw(ps, w); if (w->prop_shadow != attr_shadow_old) win_determine_shadow(ps, w); } static void win_set_shadow(session_t *ps, win *w, bool shadow_new) { if (w->shadow == shadow_new) return; w->shadow = shadow_new; // Window extents need update on shadow state change // Shadow geometry currently doesn't change on shadow state change // calc_shadow_geometry(ps, w); if (w->extents) { // Mark the old extents as damaged if the shadow is removed if (!w->shadow) add_damage(ps, w->extents); else free_region(ps, &w->extents); w->extents = win_extents(ps, w); // Mark the new extents as damaged if the shadow is added if (w->shadow) add_damage_win(ps, w); } } /** * Determine if a window should have shadow, and update things depending * on shadow state. */ static void win_determine_shadow(session_t *ps, win *w) { bool shadow_new = w->shadow; if (UNSET != w->shadow_force) shadow_new = w->shadow_force; else if (IsViewable == w->a.map_state) shadow_new = (ps->o.wintype_shadow[w->window_type] && !win_match(ps, w, ps->o.shadow_blacklist, &w->cache_sblst) && !(ps->o.shadow_ignore_shaped && w->bounding_shaped && !w->rounded_corners) && !(ps->o.respect_prop_shadow && 0 == w->prop_shadow)); win_set_shadow(ps, w, shadow_new); } static void win_set_invert_color(session_t *ps, win *w, bool invert_color_new) { if (w->invert_color == invert_color_new) return; w->invert_color = invert_color_new; add_damage_win(ps, w); } /** * Determine if a window should have color inverted. */ static void win_determine_invert_color(session_t *ps, win *w) { bool invert_color_new = w->invert_color; if (UNSET != w->invert_color_force) invert_color_new = w->invert_color_force; else if (IsViewable == w->a.map_state) invert_color_new = win_match(ps, w, ps->o.invert_color_list, &w->cache_ivclst); win_set_invert_color(ps, w, invert_color_new); } static void win_set_blur_background(session_t *ps, win *w, bool blur_background_new) { if (w->blur_background == blur_background_new) return; w->blur_background = blur_background_new; // Only consider window damaged if it's previously painted with background // blurred if (!win_is_solid(ps, w) || (ps->o.blur_background_frame && w->frame_opacity)) add_damage_win(ps, w); } /** * Determine if a window should have background blurred. */ static void win_determine_blur_background(session_t *ps, win *w) { if (IsViewable != w->a.map_state) return; bool blur_background_new = ps->o.blur_background && !win_match(ps, w, ps->o.blur_background_blacklist, &w->cache_bbblst); win_set_blur_background(ps, w, blur_background_new); } /** * Update window opacity according to opacity rules. */ static void win_update_opacity_rule(session_t *ps, win *w) { if (IsViewable != w->a.map_state) return; #ifdef CONFIG_C2 // If long is 32-bit, unfortunately there's no way could we express "unset", // so we just entirely don't distinguish "unset" and OPAQUE opacity_t opacity = OPAQUE; void *val = NULL; if (c2_matchd(ps, w, ps->o.opacity_rules, &w->cache_oparule, &val)) opacity = ((double) (long) val) / 100.0 * OPAQUE; if (opacity == w->opacity_set) return; if (OPAQUE != opacity) wid_set_opacity_prop(ps, w->id, opacity); else if (OPAQUE != w->opacity_set) wid_rm_opacity_prop(ps, w->id); w->opacity_set = opacity; #endif } /** * Function to be called on window type changes. */ static void win_on_wtype_change(session_t *ps, win *w) { win_determine_shadow(ps, w); win_determine_fade(ps, w); win_update_focused(ps, w); if (ps->o.invert_color_list) win_determine_invert_color(ps, w); if (ps->o.opacity_rules) win_update_opacity_rule(ps, w); } /** * Function to be called on window data changes. */ static void win_on_factor_change(session_t *ps, win *w) { if (ps->o.shadow_blacklist) win_determine_shadow(ps, w); if (ps->o.fade_blacklist) win_determine_fade(ps, w); if (ps->o.invert_color_list) win_determine_invert_color(ps, w); if (ps->o.focus_blacklist) win_update_focused(ps, w); if (ps->o.blur_background_blacklist) win_determine_blur_background(ps, w); if (ps->o.opacity_rules) win_update_opacity_rule(ps, w); if (IsViewable == w->a.map_state && ps->o.paint_blacklist) w->paint_excluded = win_match(ps, w, ps->o.paint_blacklist, &w->cache_pblst); if (IsViewable == w->a.map_state && ps->o.unredir_if_possible_blacklist) w->unredir_if_possible_excluded = win_match(ps, w, ps->o.unredir_if_possible_blacklist, &w->cache_uipblst); } /** * Process needed window updates. */ static void win_upd_run(session_t *ps, win *w, win_upd_t *pupd) { if (pupd->shadow) { win_determine_shadow(ps, w); pupd->shadow = false; } if (pupd->fade) { win_determine_fade(ps, w); pupd->fade = false; } if (pupd->invert_color) { win_determine_invert_color(ps, w); pupd->invert_color = false; } if (pupd->focus) { win_update_focused(ps, w); pupd->focus = false; } } /** * Update cache data in struct _win that depends on window size. */ static void calc_win_size(session_t *ps, win *w) { w->widthb = w->a.width + w->a.border_width * 2; w->heightb = w->a.height + w->a.border_width * 2; calc_shadow_geometry(ps, w); w->flags |= WFLAG_SIZE_CHANGE; } /** * Calculate and update geometry of the shadow of a window. */ static void calc_shadow_geometry(session_t *ps, win *w) { w->shadow_dx = ps->o.shadow_offset_x; w->shadow_dy = ps->o.shadow_offset_y; w->shadow_width = w->widthb + ps->gaussian_map->size; w->shadow_height = w->heightb + ps->gaussian_map->size; } /** * Update window type. */ static void win_upd_wintype(session_t *ps, win *w) { const wintype_t wtype_old = w->window_type; // Detect window type here w->window_type = wid_get_prop_wintype(ps, w->client_win); // Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take // override-redirect windows or windows without WM_TRANSIENT_FOR as // _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG. if (WINTYPE_UNKNOWN == w->window_type) { if (w->a.override_redirect || !wid_has_prop(ps, w->client_win, ps->atom_transient)) w->window_type = WINTYPE_NORMAL; else w->window_type = WINTYPE_DIALOG; } if (w->window_type != wtype_old) win_on_wtype_change(ps, w); } /** * Mark a window as the client window of another. * * @param ps current session * @param w struct _win of the parent window * @param client window ID of the client window */ static void win_mark_client(session_t *ps, win *w, Window client) { w->client_win = client; // If the window isn't mapped yet, stop here, as the function will be // called in map_win() if (IsViewable != w->a.map_state) return; XSelectInput(ps->dpy, client, determine_evmask(ps, client, WIN_EVMODE_CLIENT)); // Make sure the XSelectInput() requests are sent XFlush(ps->dpy); win_upd_wintype(ps, w); // Get frame widths. The window is in damaged area already. if (ps->o.frame_opacity) get_frame_extents(ps, w, client); // Get window group if (ps->o.track_leader) win_update_leader(ps, w); // Get window name and class if we are tracking them if (ps->o.track_wdata) { win_get_name(ps, w); win_get_class(ps, w); win_get_role(ps, w); } // Update everything related to conditions win_on_factor_change(ps, w); // Update window focus state win_update_focused(ps, w); } /** * Unmark current client window of a window. * * @param ps current session * @param w struct _win of the parent window */ static void win_unmark_client(session_t *ps, win *w) { Window client = w->client_win; w->client_win = None; // Recheck event mask XSelectInput(ps->dpy, client, determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)); } /** * Recheck client window of a window. * * @param ps current session * @param w struct _win of the parent window */ static void win_recheck_client(session_t *ps, win *w) { // Initialize wmwin to false w->wmwin = false; // Look for the client window // Always recursively look for a window with WM_STATE, as Fluxbox // sets override-redirect flags on all frame windows. Window cw = find_client_win(ps, w->id); #ifdef DEBUG_CLIENTWIN if (cw) printf_dbgf("(%#010lx): client %#010lx\n", w->id, cw); #endif // Set a window's client window to itself if we couldn't find a // client window if (!cw) { cw = w->id; w->wmwin = !w->a.override_redirect; #ifdef DEBUG_CLIENTWIN printf_dbgf("(%#010lx): client self (%s)\n", w->id, (w->wmwin ? "wmwin": "override-redirected")); #endif } // Unmark the old one if (w->client_win && w->client_win != cw) win_unmark_client(ps, w); // Mark the new one win_mark_client(ps, w, cw); } static bool add_win(session_t *ps, Window id, Window prev) { const static win win_def = { .next = NULL, .prev_trans = NULL, .id = None, .a = { }, #ifdef CONFIG_XINERAMA .xinerama_scr = -1, #endif .pictfmt = NULL, .mode = WMODE_TRANS, .damaged = false, .damage = None, .pixmap_damaged = false, .paint = PAINT_INIT, .border_size = None, .extents = None, .flags = 0, .need_configure = false, .queue_configure = { }, .reg_ignore = None, .widthb = 0, .heightb = 0, .destroyed = false, .bounding_shaped = false, .rounded_corners = false, .to_paint = false, .in_openclose = false, .client_win = None, .window_type = WINTYPE_UNKNOWN, .wmwin = false, .leader = None, .cache_leader = None, .focused = false, .focused_force = UNSET, .name = NULL, .class_instance = NULL, .class_general = NULL, .role = NULL, .cache_sblst = NULL, .cache_fblst = NULL, .cache_fcblst = NULL, .cache_ivclst = NULL, .cache_bbblst = NULL, .cache_oparule = NULL, .opacity = 0, .opacity_tgt = 0, .opacity_prop = OPAQUE, .opacity_prop_client = OPAQUE, .opacity_set = OPAQUE, .fade = false, .fade_force = UNSET, .fade_callback = NULL, .frame_opacity = 0.0, .frame_extents = MARGIN_INIT, .shadow = false, .shadow_force = UNSET, .shadow_opacity = 0.0, .shadow_dx = 0, .shadow_dy = 0, .shadow_width = 0, .shadow_height = 0, .shadow_paint = PAINT_INIT, .prop_shadow = -1, .dim = false, .invert_color = false, .invert_color_force = UNSET, .blur_background = false, }; // Reject overlay window and already added windows if (id == ps->overlay || find_win(ps, id)) { return false; } // Allocate and initialize the new win structure win *new = malloc(sizeof(win)); #ifdef DEBUG_EVENTS printf_dbgf("(%#010lx): %p\n", id, new); #endif if (!new) { printf_errf("(%#010lx): Failed to allocate memory for the new window.", id); return false; } memcpy(new, &win_def, sizeof(win)); // Find window insertion point win **p = NULL; if (prev) { for (p = &ps->list; *p; p = &(*p)->next) { if ((*p)->id == prev && !(*p)->destroyed) break; } } else { p = &ps->list; } // Fill structure new->id = id; set_ignore_next(ps); if (!XGetWindowAttributes(ps->dpy, id, &new->a) || IsUnviewable == new->a.map_state) { // Failed to get window attributes probably means the window is gone // already. IsUnviewable means the window is already reparented // elsewhere. free(new); return false; } // Delay window mapping int map_state = new->a.map_state; assert(IsViewable == map_state || IsUnmapped == map_state); new->a.map_state = IsUnmapped; if (InputOutput == new->a.class) { // Get window picture format new->pictfmt = XRenderFindVisualFormat(ps->dpy, new->a.visual); // Create Damage for window set_ignore_next(ps); new->damage = XDamageCreate(ps->dpy, id, XDamageReportNonEmpty); } calc_win_size(ps, new); new->next = *p; *p = new; #ifdef CONFIG_DBUS // Send D-Bus signal if (ps->o.dbus) { cdbus_ev_win_added(ps, new); } #endif if (IsViewable == map_state) { map_win(ps, id); } return true; } static void restack_win(session_t *ps, win *w, Window new_above) { Window old_above; update_reg_ignore_expire(ps, w); if (w->next) { old_above = w->next->id; } else { old_above = None; } if (old_above != new_above) { win **prev = NULL, **prev_old = NULL; // unhook for (prev = &ps->list; *prev; prev = &(*prev)->next) { if ((*prev) == w) break; } prev_old = prev; bool found = false; // rehook for (prev = &ps->list; *prev; prev = &(*prev)->next) { if ((*prev)->id == new_above && !(*prev)->destroyed) { found = true; break; } } if (new_above && !found) { printf_errf("(%#010lx, %#010lx): " "Failed to found new above window.", w->id, new_above); return; } *prev_old = w->next; w->next = *prev; *prev = w; #ifdef DEBUG_RESTACK { const char *desc; char *window_name = NULL; bool to_free; win* c = ps->list; printf_dbgf("(%#010lx, %#010lx): " "Window stack modified. Current stack:\n", w->id, new_above); for (; c; c = c->next) { window_name = "(Failed to get title)"; to_free = ev_window_name(ps, c->id, &window_name); desc = ""; if (c->destroyed) desc = "(D) "; printf("%#010lx \"%s\" %s", c->id, window_name, desc); if (c->next) printf("-> "); if (to_free) { cxfree(window_name); window_name = NULL; } } fputs("\n", stdout); } #endif } } static bool init_filters(session_t *ps); static void configure_win(session_t *ps, XConfigureEvent *ce) { // On root window changes if (ce->window == ps->root) { free_paint(ps, &ps->tgt_buffer); ps->root_width = ce->width; ps->root_height = ce->height; rebuild_screen_reg(ps); rebuild_shadow_exclude_reg(ps); free_all_damage_last(ps); // Re-redirect screen if required if (ps->o.reredir_on_root_change && ps->redirected) { redir_stop(ps); redir_start(ps); } #ifdef CONFIG_VSYNC_OPENGL // Reinitialize GLX on root change if (ps->o.glx_reinit_on_root_change && ps->psglx) { if (!glx_reinit(ps, bkend_use_glx(ps))) printf_errf("(): Failed to reinitialize GLX, troubles ahead."); if (BKEND_GLX == ps->o.backend && !init_filters(ps)) printf_errf("(): Failed to initialize filters."); } // GLX root change callback if (BKEND_GLX == ps->o.backend) glx_on_root_change(ps); #endif force_repaint(ps); return; } // Other window changes win *w = find_win(ps, ce->window); XserverRegion damage = None; if (!w) return; if (w->a.map_state == IsUnmapped) { /* save the configure event for when the window maps */ w->need_configure = true; w->queue_configure = *ce; restack_win(ps, w, ce->above); } else { if (!(w->need_configure)) { restack_win(ps, w, ce->above); } bool factor_change = false; // Windows restack (including window restacks happened when this // window is not mapped) could mess up all reg_ignore ps->reg_ignore_expire = true; w->need_configure = false; damage = XFixesCreateRegion(ps->dpy, 0, 0); if (w->extents != None) { XFixesCopyRegion(ps->dpy, damage, w->extents); } // If window geometry did not change, don't free extents here if (w->a.x != ce->x || w->a.y != ce->y || w->a.width != ce->width || w->a.height != ce->height || w->a.border_width != ce->border_width) { factor_change = true; free_region(ps, &w->extents); free_region(ps, &w->border_size); } w->a.x = ce->x; w->a.y = ce->y; if (w->a.width != ce->width || w->a.height != ce->height || w->a.border_width != ce->border_width) free_wpaint(ps, w); if (w->a.width != ce->width || w->a.height != ce->height || w->a.border_width != ce->border_width) { w->a.width = ce->width; w->a.height = ce->height; w->a.border_width = ce->border_width; calc_win_size(ps, w); // Rounded corner detection is affected by window size if (ps->shape_exists && ps->o.shadow_ignore_shaped && ps->o.detect_rounded_corners && w->bounding_shaped) win_update_shape(ps, w); } if (damage) { XserverRegion extents = win_extents(ps, w); XFixesUnionRegion(ps->dpy, damage, damage, extents); XFixesDestroyRegion(ps->dpy, extents); add_damage(ps, damage); } if (factor_change) { cxinerama_win_upd_scr(ps, w); win_on_factor_change(ps, w); } } // override_redirect flag cannot be changed after window creation, as far // as I know, so there's no point to re-match windows here. w->a.override_redirect = ce->override_redirect; } static void circulate_win(session_t *ps, XCirculateEvent *ce) { win *w = find_win(ps, ce->window); Window new_above; if (!w) return; if (ce->place == PlaceOnTop) { new_above = ps->list->id; } else { new_above = None; } restack_win(ps, w, new_above); } static void finish_destroy_win(session_t *ps, Window id) { win **prev = NULL, *w = NULL; #ifdef DEBUG_EVENTS printf_dbgf("(%#010lx): Starting...\n", id); #endif for (prev = &ps->list; (w = *prev); prev = &w->next) { if (w->id == id && w->destroyed) { #ifdef DEBUG_EVENTS printf_dbgf("(%#010lx \"%s\"): %p\n", id, w->name, w); #endif finish_unmap_win(ps, w); *prev = w->next; // Clear active_win if it's pointing to the destroyed window if (w == ps->active_win) ps->active_win = NULL; free_win_res(ps, w); // Drop w from all prev_trans to avoid accessing freed memory in // repair_win() for (win *w2 = ps->list; w2; w2 = w2->next) if (w == w2->prev_trans) w2->prev_trans = NULL; free(w); break; } } } static void destroy_callback(session_t *ps, win *w) { finish_destroy_win(ps, w->id); } static void destroy_win(session_t *ps, Window id) { win *w = find_win(ps, id); #ifdef DEBUG_EVENTS printf_dbgf("(%#010lx \"%s\"): %p\n", id, (w ? w->name: NULL), w); #endif if (w) { unmap_win(ps, w); w->destroyed = true; if (ps->o.no_fading_destroyed_argb) win_determine_fade(ps, w); // Set fading callback set_fade_callback(ps, w, destroy_callback, false); #ifdef CONFIG_DBUS // Send D-Bus signal if (ps->o.dbus) { cdbus_ev_win_destroyed(ps, w); } #endif } } static inline void root_damaged(session_t *ps) { if (ps->root_tile_paint.pixmap) { XClearArea(ps->dpy, ps->root, 0, 0, 0, 0, true); // if (ps->root_picture != ps->root_tile) { free_root_tile(ps); /* } if (root_damage) { XserverRegion parts = XFixesCreateRegion(ps->dpy, 0, 0); XDamageSubtract(ps->dpy, root_damage, None, parts); add_damage(ps, parts); } */ } // Mark screen damaged force_repaint(ps); } static void damage_win(session_t *ps, XDamageNotifyEvent *de) { /* if (ps->root == de->drawable) { root_damaged(); return; } */ win *w = find_win(ps, de->drawable); if (!w) return; repair_win(ps, w); } /** * Xlib error handler function. */ static int xerror(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { session_t * const ps = ps_g; int o = 0; const char *name = "Unknown"; if (should_ignore(ps, ev->serial)) { return 0; } if (ev->request_code == ps->composite_opcode && ev->minor_code == X_CompositeRedirectSubwindows) { fprintf(stderr, "Another composite manager is already running\n"); exit(1); } #define CASESTRRET2(s) case s: name = #s; break o = ev->error_code - ps->xfixes_error; switch (o) { CASESTRRET2(BadRegion); } o = ev->error_code - ps->damage_error; switch (o) { CASESTRRET2(BadDamage); } o = ev->error_code - ps->render_error; switch (o) { CASESTRRET2(BadPictFormat); CASESTRRET2(BadPicture); CASESTRRET2(BadPictOp); CASESTRRET2(BadGlyphSet); CASESTRRET2(BadGlyph); } #ifdef CONFIG_VSYNC_OPENGL if (ps->glx_exists) { o = ev->error_code - ps->glx_error; switch (o) { CASESTRRET2(GLX_BAD_SCREEN); CASESTRRET2(GLX_BAD_ATTRIBUTE); CASESTRRET2(GLX_NO_EXTENSION); CASESTRRET2(GLX_BAD_VISUAL); CASESTRRET2(GLX_BAD_CONTEXT); CASESTRRET2(GLX_BAD_VALUE); CASESTRRET2(GLX_BAD_ENUM); } } #endif #ifdef CONFIG_XSYNC if (ps->xsync_exists) { o = ev->error_code - ps->xsync_error; switch (o) { CASESTRRET2(XSyncBadCounter); CASESTRRET2(XSyncBadAlarm); CASESTRRET2(XSyncBadFence); } } #endif switch (ev->error_code) { CASESTRRET2(BadAccess); CASESTRRET2(BadAlloc); CASESTRRET2(BadAtom); CASESTRRET2(BadColor); CASESTRRET2(BadCursor); CASESTRRET2(BadDrawable); CASESTRRET2(BadFont); CASESTRRET2(BadGC); CASESTRRET2(BadIDChoice); CASESTRRET2(BadImplementation); CASESTRRET2(BadLength); CASESTRRET2(BadMatch); CASESTRRET2(BadName); CASESTRRET2(BadPixmap); CASESTRRET2(BadRequest); CASESTRRET2(BadValue); CASESTRRET2(BadWindow); } #undef CASESTRRET2 print_timestamp(ps); { char buf[BUF_LEN] = ""; XGetErrorText(ps->dpy, ev->error_code, buf, BUF_LEN); printf("error %4d %-12s request %4d minor %4d serial %6lu: \"%s\"\n", ev->error_code, name, ev->request_code, ev->minor_code, ev->serial, buf); } // print_backtrace(); return 0; } static void expose_root(session_t *ps, XRectangle *rects, int nrects) { free_all_damage_last(ps); XserverRegion region = XFixesCreateRegion(ps->dpy, rects, nrects); add_damage(ps, region); } /** * Get the value of a type-Window property of a window. * * @return the value if successful, 0 otherwise */ static Window wid_get_prop_window(session_t *ps, Window wid, Atom aprop) { // Get the attribute Window p = None; winprop_t prop = wid_get_prop(ps, wid, aprop, 1L, XA_WINDOW, 32); // Return it if (prop.nitems) { p = *prop.data.p32; } free_winprop(&prop); return p; } /** * Update focused state of a window. */ static void win_update_focused(session_t *ps, win *w) { bool focused_old = w->focused; if (UNSET != w->focused_force) { w->focused = w->focused_force; } else { w->focused = win_is_focused_real(ps, w); // Use wintype_focus, and treat WM windows and override-redirected // windows specially if (ps->o.wintype_focus[w->window_type] || (ps->o.mark_wmwin_focused && w->wmwin) || (ps->o.mark_ovredir_focused && w->id == w->client_win && !w->wmwin) || (IsViewable == w->a.map_state && win_match(ps, w, ps->o.focus_blacklist, &w->cache_fcblst))) w->focused = true; // If window grouping detection is enabled, mark the window active if // its group is if (ps->o.track_leader && ps->active_leader && win_get_leader(ps, w) == ps->active_leader) { w->focused = true; } } // Always recalculate the window target opacity, since some opacity-related // options depend on the output value of win_is_focused_real() instead of // w->focused w->flags |= WFLAG_OPCT_CHANGE; } /** * Set real focused state of a window. */ static void win_set_focused(session_t *ps, win *w, bool focused) { // Unmapped windows will have their focused state reset on map if (IsUnmapped == w->a.map_state) return; if (win_is_focused_real(ps, w) == focused) return; if (focused) { if (ps->active_win) win_set_focused(ps, ps->active_win, false); ps->active_win = w; } else if (w == ps->active_win) ps->active_win = NULL; assert(win_is_focused_real(ps, w) == focused); win_on_focus_change(ps, w); } /** * Handle window focus change. */ static void win_on_focus_change(session_t *ps, win *w) { // If window grouping detection is enabled if (ps->o.track_leader) { Window leader = win_get_leader(ps, w); // If the window gets focused, replace the old active_leader if (win_is_focused_real(ps, w) && leader != ps->active_leader) { Window active_leader_old = ps->active_leader; ps->active_leader = leader; group_update_focused(ps, active_leader_old); group_update_focused(ps, leader); } // If the group get unfocused, remove it from active_leader else if (!win_is_focused_real(ps, w) && leader && leader == ps->active_leader && !group_is_focused(ps, leader)) { ps->active_leader = None; group_update_focused(ps, leader); } // The window itself must be updated anyway win_update_focused(ps, w); } // Otherwise, only update the window itself else { win_update_focused(ps, w); } // Update everything related to conditions win_on_factor_change(ps, w); #ifdef CONFIG_DBUS // Send D-Bus signal if (ps->o.dbus) { if (win_is_focused_real(ps, w)) cdbus_ev_win_focusin(ps, w); else cdbus_ev_win_focusout(ps, w); } #endif } /** * Update leader of a window. */ static void win_update_leader(session_t *ps, win *w) { Window leader = None; // Read the leader properties if (ps->o.detect_transient && !leader) leader = wid_get_prop_window(ps, w->client_win, ps->atom_transient); if (ps->o.detect_client_leader && !leader) leader = wid_get_prop_window(ps, w->client_win, ps->atom_client_leader); win_set_leader(ps, w, leader); #ifdef DEBUG_LEADER printf_dbgf("(%#010lx): client %#010lx, leader %#010lx, cache %#010lx\n", w->id, w->client_win, w->leader, win_get_leader(ps, w)); #endif } /** * Set leader of a window. */ static void win_set_leader(session_t *ps, win *w, Window nleader) { // If the leader changes if (w->leader != nleader) { Window cache_leader_old = win_get_leader(ps, w); w->leader = nleader; // Forcefully do this to deal with the case when a child window // gets mapped before parent, or when the window is a waypoint clear_cache_win_leaders(ps); // Update the old and new window group and active_leader if the window // could affect their state. Window cache_leader = win_get_leader(ps, w); if (win_is_focused_real(ps, w) && cache_leader_old != cache_leader) { ps->active_leader = cache_leader; group_update_focused(ps, cache_leader_old); group_update_focused(ps, cache_leader); } // Otherwise, at most the window itself is affected else { win_update_focused(ps, w); } // Update everything related to conditions win_on_factor_change(ps, w); } } /** * Internal function of win_get_leader(). */ static Window win_get_leader_raw(session_t *ps, win *w, int recursions) { // Rebuild the cache if needed if (!w->cache_leader && (w->client_win || w->leader)) { // Leader defaults to client window if (!(w->cache_leader = w->leader)) w->cache_leader = w->client_win; // If the leader of this window isn't itself, look for its ancestors if (w->cache_leader && w->cache_leader != w->client_win) { win *wp = find_toplevel(ps, w->cache_leader); if (wp) { // Dead loop? if (recursions > WIN_GET_LEADER_MAX_RECURSION) return None; w->cache_leader = win_get_leader_raw(ps, wp, recursions + 1); } } } return w->cache_leader; } /** * Get the value of a text property of a window. */ bool wid_get_text_prop(session_t *ps, Window wid, Atom prop, char ***pstrlst, int *pnstr) { XTextProperty text_prop = { NULL, None, 0, 0 }; if (!(XGetTextProperty(ps->dpy, wid, &text_prop, prop) && text_prop.value)) return false; if (Success != XmbTextPropertyToTextList(ps->dpy, &text_prop, pstrlst, pnstr) || !*pnstr) { *pnstr = 0; if (*pstrlst) XFreeStringList(*pstrlst); cxfree(text_prop.value); return false; } cxfree(text_prop.value); return true; } /** * Get the name of a window from window ID. */ static bool wid_get_name(session_t *ps, Window wid, char **name) { XTextProperty text_prop = { NULL, None, 0, 0 }; char **strlst = NULL; int nstr = 0; if (!(wid_get_text_prop(ps, wid, ps->atom_name_ewmh, &strlst, &nstr))) { #ifdef DEBUG_WINDATA printf_dbgf("(%#010lx): _NET_WM_NAME unset, falling back to WM_NAME.\n", wid); #endif if (!(XGetWMName(ps->dpy, wid, &text_prop) && text_prop.value)) { return false; } if (Success != XmbTextPropertyToTextList(ps->dpy, &text_prop, &strlst, &nstr) || !nstr || !strlst) { if (strlst) XFreeStringList(strlst); cxfree(text_prop.value); return false; } cxfree(text_prop.value); } *name = mstrcpy(strlst[0]); XFreeStringList(strlst); return true; } /** * Get the role of a window from window ID. */ static bool wid_get_role(session_t *ps, Window wid, char **role) { char **strlst = NULL; int nstr = 0; if (!wid_get_text_prop(ps, wid, ps->atom_role, &strlst, &nstr)) { return false; } *role = mstrcpy(strlst[0]); XFreeStringList(strlst); return true; } /** * Retrieve a string property of a window and update its win * structure. */ static int win_get_prop_str(session_t *ps, win *w, char **tgt, bool (*func_wid_get_prop_str)(session_t *ps, Window wid, char **tgt)) { int ret = -1; char *prop_old = *tgt; // Can't do anything if there's no client window if (!w->client_win) return false; // Get the property ret = func_wid_get_prop_str(ps, w->client_win, tgt); // Return -1 if func_wid_get_prop_str() failed, 0 if the property // doesn't change, 1 if it changes if (!ret) ret = -1; else if (prop_old && !strcmp(*tgt, prop_old)) ret = 0; else ret = 1; // Keep the old property if there's no new one if (*tgt != prop_old) free(prop_old); return ret; } /** * Retrieve the WM_CLASS of a window and update its * win structure. */ static bool win_get_class(session_t *ps, win *w) { char **strlst = NULL; int nstr = 0; // Can't do anything if there's no client window if (!w->client_win) return false; // Free and reset old strings free(w->class_instance); free(w->class_general); w->class_instance = NULL; w->class_general = NULL; // Retrieve the property string list if (!wid_get_text_prop(ps, w->client_win, ps->atom_class, &strlst, &nstr)) return false; // Copy the strings if successful w->class_instance = mstrcpy(strlst[0]); if (nstr > 1) w->class_general = mstrcpy(strlst[1]); XFreeStringList(strlst); #ifdef DEBUG_WINDATA printf_dbgf("(%#010lx): client = %#010lx, " "instance = \"%s\", general = \"%s\"\n", w->id, w->client_win, w->class_instance, w->class_general); #endif return true; } /** * Force a full-screen repaint. */ void force_repaint(session_t *ps) { assert(ps->screen_reg); XserverRegion reg = None; if (ps->screen_reg && (reg = copy_region(ps, ps->screen_reg))) { ps->ev_received = true; add_damage(ps, reg); } } #ifdef CONFIG_DBUS /** @name DBus hooks */ ///@{ /** * Set w->shadow_force of a window. */ void win_set_shadow_force(session_t *ps, win *w, switch_t val) { if (val != w->shadow_force) { w->shadow_force = val; win_determine_shadow(ps, w); ps->ev_received = true; } } /** * Set w->fade_force of a window. */ void win_set_fade_force(session_t *ps, win *w, switch_t val) { if (val != w->fade_force) { w->fade_force = val; win_determine_fade(ps, w); ps->ev_received = true; } } /** * Set w->focused_force of a window. */ void win_set_focused_force(session_t *ps, win *w, switch_t val) { if (val != w->focused_force) { w->focused_force = val; win_update_focused(ps, w); ps->ev_received = true; } } /** * Set w->invert_color_force of a window. */ void win_set_invert_color_force(session_t *ps, win *w, switch_t val) { if (val != w->invert_color_force) { w->invert_color_force = val; win_determine_invert_color(ps, w); ps->ev_received = true; } } /** * Enable focus tracking. */ void opts_init_track_focus(session_t *ps) { // Already tracking focus if (ps->o.track_focus) return; ps->o.track_focus = true; if (!ps->o.use_ewmh_active_win) { // Start listening to FocusChange events for (win *w = ps->list; w; w = w->next) if (IsViewable == w->a.map_state) XSelectInput(ps->dpy, w->id, determine_evmask(ps, w->id, WIN_EVMODE_FRAME)); } // Recheck focus recheck_focus(ps); } /** * Set no_fading_openclose option. */ void opts_set_no_fading_openclose(session_t *ps, bool newval) { if (newval != ps->o.no_fading_openclose) { ps->o.no_fading_openclose = newval; for (win *w = ps->list; w; w = w->next) win_determine_fade(ps, w); ps->ev_received = true; } } //!@} #endif #ifdef DEBUG_EVENTS static int ev_serial(XEvent *ev) { if ((ev->type & 0x7f) != KeymapNotify) { return ev->xany.serial; } return NextRequest(ev->xany.display); } static const char * ev_name(session_t *ps, XEvent *ev) { static char buf[128]; switch (ev->type & 0x7f) { CASESTRRET(FocusIn); CASESTRRET(FocusOut); CASESTRRET(CreateNotify); CASESTRRET(ConfigureNotify); CASESTRRET(DestroyNotify); CASESTRRET(MapNotify); CASESTRRET(UnmapNotify); CASESTRRET(ReparentNotify); CASESTRRET(CirculateNotify); CASESTRRET(Expose); CASESTRRET(PropertyNotify); CASESTRRET(ClientMessage); } if (isdamagenotify(ps, ev)) return "Damage"; if (ps->shape_exists && ev->type == ps->shape_event) return "ShapeNotify"; #ifdef CONFIG_XSYNC if (ps->xsync_exists) { int o = ev->type - ps->xsync_event; switch (o) { CASESTRRET(XSyncCounterNotify); CASESTRRET(XSyncAlarmNotify); } } #endif sprintf(buf, "Event %d", ev->type); return buf; } static Window ev_window(session_t *ps, XEvent *ev) { switch (ev->type) { case FocusIn: case FocusOut: return ev->xfocus.window; case CreateNotify: return ev->xcreatewindow.window; case ConfigureNotify: return ev->xconfigure.window; case DestroyNotify: return ev->xdestroywindow.window; case MapNotify: return ev->xmap.window; case UnmapNotify: return ev->xunmap.window; case ReparentNotify: return ev->xreparent.window; case CirculateNotify: return ev->xcirculate.window; case Expose: return ev->xexpose.window; case PropertyNotify: return ev->xproperty.window; case ClientMessage: return ev->xclient.window; default: if (isdamagenotify(ps, ev)) { return ((XDamageNotifyEvent *)ev)->drawable; } if (ps->shape_exists && ev->type == ps->shape_event) { return ((XShapeEvent *) ev)->window; } return 0; } } static inline const char * ev_focus_mode_name(XFocusChangeEvent* ev) { switch (ev->mode) { CASESTRRET(NotifyNormal); CASESTRRET(NotifyWhileGrabbed); CASESTRRET(NotifyGrab); CASESTRRET(NotifyUngrab); } return "Unknown"; } static inline const char * ev_focus_detail_name(XFocusChangeEvent* ev) { switch (ev->detail) { CASESTRRET(NotifyAncestor); CASESTRRET(NotifyVirtual); CASESTRRET(NotifyInferior); CASESTRRET(NotifyNonlinear); CASESTRRET(NotifyNonlinearVirtual); CASESTRRET(NotifyPointer); CASESTRRET(NotifyPointerRoot); CASESTRRET(NotifyDetailNone); } return "Unknown"; } static inline void ev_focus_report(XFocusChangeEvent* ev) { printf(" { mode: %s, detail: %s }\n", ev_focus_mode_name(ev), ev_focus_detail_name(ev)); } #endif // === Events === /** * Determine whether we should respond to a FocusIn/Out * event. */ /* inline static bool ev_focus_accept(XFocusChangeEvent *ev) { return NotifyNormal == ev->mode || NotifyUngrab == ev->mode; } */ static inline void ev_focus_in(session_t *ps, XFocusChangeEvent *ev) { #ifdef DEBUG_EVENTS ev_focus_report(ev); #endif recheck_focus(ps); } inline static void ev_focus_out(session_t *ps, XFocusChangeEvent *ev) { #ifdef DEBUG_EVENTS ev_focus_report(ev); #endif recheck_focus(ps); } inline static void ev_create_notify(session_t *ps, XCreateWindowEvent *ev) { assert(ev->parent == ps->root); add_win(ps, ev->window, 0); } inline static void ev_configure_notify(session_t *ps, XConfigureEvent *ev) { #ifdef DEBUG_EVENTS printf(" { send_event: %d, " " above: %#010lx, " " override_redirect: %d }\n", ev->send_event, ev->above, ev->override_redirect); #endif configure_win(ps, ev); } inline static void ev_destroy_notify(session_t *ps, XDestroyWindowEvent *ev) { destroy_win(ps, ev->window); } inline static void ev_map_notify(session_t *ps, XMapEvent *ev) { map_win(ps, ev->window); } inline static void ev_unmap_notify(session_t *ps, XUnmapEvent *ev) { win *w = find_win(ps, ev->window); if (w) unmap_win(ps, w); } inline static void ev_reparent_notify(session_t *ps, XReparentEvent *ev) { #ifdef DEBUG_EVENTS printf_dbg(" { new_parent: %#010lx, override_redirect: %d }\n", ev->parent, ev->override_redirect); #endif if (ev->parent == ps->root) { add_win(ps, ev->window, 0); } else { destroy_win(ps, ev->window); // Reset event mask in case something wrong happens XSelectInput(ps->dpy, ev->window, determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)); // Check if the window is an undetected client window // Firstly, check if it's a known client window if (!find_toplevel(ps, ev->window)) { // If not, look for its frame window win *w_top = find_toplevel2(ps, ev->parent); // If found, and the client window has not been determined, or its // frame may not have a correct client, continue if (w_top && (!w_top->client_win || w_top->client_win == w_top->id)) { // If it has WM_STATE, mark it the client window if (wid_has_prop(ps, ev->window, ps->atom_client)) { w_top->wmwin = false; win_unmark_client(ps, w_top); win_mark_client(ps, w_top, ev->window); } // Otherwise, watch for WM_STATE on it else { XSelectInput(ps->dpy, ev->window, determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) | PropertyChangeMask); } } } } } inline static void ev_circulate_notify(session_t *ps, XCirculateEvent *ev) { circulate_win(ps, ev); } inline static void ev_expose(session_t *ps, XExposeEvent *ev) { if (ev->window == ps->root || (ps->overlay && ev->window == ps->overlay)) { int more = ev->count + 1; if (ps->n_expose == ps->size_expose) { if (ps->expose_rects) { ps->expose_rects = realloc(ps->expose_rects, (ps->size_expose + more) * sizeof(XRectangle)); ps->size_expose += more; } else { ps->expose_rects = malloc(more * sizeof(XRectangle)); ps->size_expose = more; } } ps->expose_rects[ps->n_expose].x = ev->x; ps->expose_rects[ps->n_expose].y = ev->y; ps->expose_rects[ps->n_expose].width = ev->width; ps->expose_rects[ps->n_expose].height = ev->height; ps->n_expose++; if (ev->count == 0) { expose_root(ps, ps->expose_rects, ps->n_expose); ps->n_expose = 0; } } } /** * Update current active window based on EWMH _NET_ACTIVE_WIN. * * Does not change anything if we fail to get the attribute or the window * returned could not be found. */ static void update_ewmh_active_win(session_t *ps) { // Search for the window Window wid = wid_get_prop_window(ps, ps->root, ps->atom_ewmh_active_win); win *w = find_win_all(ps, wid); // Mark the window focused. No need to unfocus the previous one. if (w) win_set_focused(ps, w, true); } inline static void ev_property_notify(session_t *ps, XPropertyEvent *ev) { #ifdef DEBUG_EVENTS { // Print out changed atom char *name = XGetAtomName(ps->dpy, ev->atom); printf_dbg(" { atom = %s }\n", name); cxfree(name); } #endif if (ps->root == ev->window) { if (ps->o.track_focus && ps->o.use_ewmh_active_win && ps->atom_ewmh_active_win == ev->atom) { update_ewmh_active_win(ps); } else { // Destroy the root "image" if the wallpaper probably changed for (int p = 0; background_props_str[p]; p++) { if (ev->atom == get_atom(ps, background_props_str[p])) { root_damaged(ps); break; } } } // Unconcerned about any other proprties on root window return; } // If WM_STATE changes if (ev->atom == ps->atom_client) { // Check whether it could be a client window if (!find_toplevel(ps, ev->window)) { // Reset event mask anyway XSelectInput(ps->dpy, ev->window, determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)); win *w_top = find_toplevel2(ps, ev->window); // Initialize client_win as early as possible if (w_top && (!w_top->client_win || w_top->client_win == w_top->id) && wid_has_prop(ps, ev->window, ps->atom_client)) { w_top->wmwin = false; win_unmark_client(ps, w_top); win_mark_client(ps, w_top, ev->window); } } } // If _NET_WM_WINDOW_TYPE changes... God knows why this would happen, but // there are always some stupid applications. (#144) if (ev->atom == ps->atom_win_type) { win *w = NULL; if ((w = find_toplevel(ps, ev->window))) win_upd_wintype(ps, w); } // If _NET_WM_OPACITY changes if (ev->atom == ps->atom_opacity) { win *w = NULL; if ((w = find_win(ps, ev->window))) w->opacity_prop = wid_get_opacity_prop(ps, w->id, OPAQUE); else if (ps->o.detect_client_opacity && (w = find_toplevel(ps, ev->window))) w->opacity_prop_client = wid_get_opacity_prop(ps, w->client_win, OPAQUE); if (w) { w->flags |= WFLAG_OPCT_CHANGE; } } // If frame extents property changes if (ps->o.frame_opacity && ev->atom == ps->atom_frame_extents) { win *w = find_toplevel(ps, ev->window); if (w) { get_frame_extents(ps, w, ev->window); // If frame extents change, the window needs repaint add_damage_win(ps, w); } } // If name changes if (ps->o.track_wdata && (ps->atom_name == ev->atom || ps->atom_name_ewmh == ev->atom)) { win *w = find_toplevel(ps, ev->window); if (w && 1 == win_get_name(ps, w)) { win_on_factor_change(ps, w); } } // If class changes if (ps->o.track_wdata && ps->atom_class == ev->atom) { win *w = find_toplevel(ps, ev->window); if (w) { win_get_class(ps, w); win_on_factor_change(ps, w); } } // If role changes if (ps->o.track_wdata && ps->atom_role == ev->atom) { win *w = find_toplevel(ps, ev->window); if (w && 1 == win_get_role(ps, w)) { win_on_factor_change(ps, w); } } // If _COMPTON_SHADOW changes if (ps->o.respect_prop_shadow && ps->atom_compton_shadow == ev->atom) { win *w = find_win(ps, ev->window); if (w) win_update_prop_shadow(ps, w); } // If a leader property changes if ((ps->o.detect_transient && ps->atom_transient == ev->atom) || (ps->o.detect_client_leader && ps->atom_client_leader == ev->atom)) { win *w = find_toplevel(ps, ev->window); if (w) { win_update_leader(ps, w); } } // Check for other atoms we are tracking for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) { if (platom->atom == ev->atom) { win *w = find_win(ps, ev->window); if (!w) w = find_toplevel(ps, ev->window); if (w) win_on_factor_change(ps, w); break; } } } inline static void ev_damage_notify(session_t *ps, XDamageNotifyEvent *ev) { damage_win(ps, ev); } inline static void ev_shape_notify(session_t *ps, XShapeEvent *ev) { win *w = find_win(ps, ev->window); if (!w || IsUnmapped == w->a.map_state) return; /* * Empty border_size may indicated an * unmapped/destroyed window, in which case * seemingly BadRegion errors would be triggered * if we attempt to rebuild border_size */ if (w->border_size) { // Mark the old border_size as damaged add_damage(ps, w->border_size); w->border_size = border_size(ps, w, true); // Mark the new border_size as damaged add_damage(ps, copy_region(ps, w->border_size)); } // Redo bounding shape detection and rounded corner detection win_update_shape(ps, w); update_reg_ignore_expire(ps, w); } /** * Handle ScreenChangeNotify events from X RandR extension. */ static void ev_screen_change_notify(session_t *ps, XRRScreenChangeNotifyEvent __attribute__((unused)) *ev) { if (ps->o.xinerama_shadow_crop) cxinerama_upd_scrs(ps); if (ps->o.sw_opti && !ps->o.refresh_rate) { update_refresh_rate(ps); if (!ps->refresh_rate) { fprintf(stderr, "ev_screen_change_notify(): Refresh rate detection " "failed, --sw-opti disabled."); ps->o.sw_opti = false; } } } #if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) /** * Get a window's name from window ID. */ static bool ev_window_name(session_t *ps, Window wid, char **name) { bool to_free = false; *name = ""; if (wid) { *name = "(Failed to get title)"; if (ps->root == wid) *name = "(Root window)"; else if (ps->overlay == wid) *name = "(Overlay)"; else { win *w = find_win(ps, wid); if (!w) w = find_toplevel(ps, wid); if (w && w->name) *name = w->name; else if (!(w && w->client_win && (to_free = wid_get_name(ps, w->client_win, name)))) to_free = wid_get_name(ps, wid, name); } } return to_free; } #endif static void ev_handle(session_t *ps, XEvent *ev) { if ((ev->type & 0x7f) != KeymapNotify) { discard_ignore(ps, ev->xany.serial); } #ifdef DEBUG_EVENTS if (!isdamagenotify(ps, ev)) { Window wid = ev_window(ps, ev); char *window_name = NULL; bool to_free = false; to_free = ev_window_name(ps, wid, &window_name); print_timestamp(ps); printf("event %10.10s serial %#010x window %#010lx \"%s\"\n", ev_name(ps, ev), ev_serial(ev), wid, window_name); if (to_free) { cxfree(window_name); window_name = NULL; } } #endif switch (ev->type) { case FocusIn: ev_focus_in(ps, (XFocusChangeEvent *)ev); break; case FocusOut: ev_focus_out(ps, (XFocusChangeEvent *)ev); break; case CreateNotify: ev_create_notify(ps, (XCreateWindowEvent *)ev); break; case ConfigureNotify: ev_configure_notify(ps, (XConfigureEvent *)ev); break; case DestroyNotify: ev_destroy_notify(ps, (XDestroyWindowEvent *)ev); break; case MapNotify: ev_map_notify(ps, (XMapEvent *)ev); break; case UnmapNotify: ev_unmap_notify(ps, (XUnmapEvent *)ev); break; case ReparentNotify: ev_reparent_notify(ps, (XReparentEvent *)ev); break; case CirculateNotify: ev_circulate_notify(ps, (XCirculateEvent *)ev); break; case Expose: ev_expose(ps, (XExposeEvent *)ev); break; case PropertyNotify: ev_property_notify(ps, (XPropertyEvent *)ev); break; default: if (ps->shape_exists && ev->type == ps->shape_event) { ev_shape_notify(ps, (XShapeEvent *) ev); break; } if (ps->randr_exists && ev->type == (ps->randr_event + RRScreenChangeNotify)) { ev_screen_change_notify(ps, (XRRScreenChangeNotifyEvent *) ev); break; } if (isdamagenotify(ps, ev)) { ev_damage_notify(ps, (XDamageNotifyEvent *) ev); break; } } } // === Main === /** * Print usage text and exit. */ static void usage(int ret) { #define WARNING_DISABLED " (DISABLED AT COMPILE TIME)" #define WARNING const static char *usage_text = "compton (" COMPTON_VERSION ")\n" "usage: compton [options]\n" "Options:\n" "\n" "-d display\n" " Which display should be managed.\n" "\n" "-r radius\n" " The blur radius for shadows. (default 12)\n" "\n" "-o opacity\n" " The translucency for shadows. (default .75)\n" "\n" "-l left-offset\n" " The left offset for shadows. (default -15)\n" "\n" "-t top-offset\n" " The top offset for shadows. (default -15)\n" "\n" "-I fade-in-step\n" " Opacity change between steps while fading in. (default 0.028)\n" "\n" "-O fade-out-step\n" " Opacity change between steps while fading out. (default 0.03)\n" "\n" "-D fade-delta-time\n" " The time between steps in a fade in milliseconds. (default 10)\n" "\n" "-m opacity\n" " The opacity for menus. (default 1.0)\n" "\n" "-c\n" " Enabled client-side shadows on windows.\n" "\n" "-C\n" " Avoid drawing shadows on dock/panel windows.\n" "\n" "-z\n" " Zero the part of the shadow's mask behind the window.\n" "\n" "-f\n" " Fade windows in/out when opening/closing and when opacity\n" " changes, unless --no-fading-openclose is used.\n" "\n" "-F\n" " Equals to -f. Deprecated.\n" "\n" "-i opacity\n" " Opacity of inactive windows. (0.1 - 1.0)\n" "\n" "-e opacity\n" " Opacity of window titlebars and borders. (0.1 - 1.0)\n" "\n" "-G\n" " Don't draw shadows on DND windows\n" "\n" "-b\n" " Daemonize process.\n" "\n" "-S\n" " Enable synchronous operation (for debugging).\n" "\n" "--show-all-xerrors\n" " Show all X errors (for debugging).\n" "\n" #undef WARNING #ifndef CONFIG_LIBCONFIG #define WARNING WARNING_DISABLED #else #define WARNING #endif "--config path\n" " Look for configuration file at the path. Use /dev/null to avoid\n" " loading configuration file." WARNING "\n" "\n" "--write-pid-path path\n" " Write process ID to a file.\n" "\n" "--shadow-red value\n" " Red color value of shadow (0.0 - 1.0, defaults to 0).\n" "\n" "--shadow-green value\n" " Green color value of shadow (0.0 - 1.0, defaults to 0).\n" "\n" "--shadow-blue value\n" " Blue color value of shadow (0.0 - 1.0, defaults to 0).\n" "\n" "--inactive-opacity-override\n" " Inactive opacity set by -i overrides value of _NET_WM_OPACITY.\n" "\n" "--inactive-dim value\n" " Dim inactive windows. (0.0 - 1.0, defaults to 0)\n" "\n" "--active-opacity opacity\n" " Default opacity for active windows. (0.0 - 1.0)\n" "\n" "--mark-wmwin-focused\n" " Try to detect WM windows and mark them as active.\n" "\n" "--shadow-exclude condition\n" " Exclude conditions for shadows.\n" "\n" "--fade-exclude condition\n" " Exclude conditions for fading.\n" "\n" "--mark-ovredir-focused\n" " Mark windows that have no WM frame as active.\n" "\n" "--no-fading-openclose\n" " Do not fade on window open/close.\n" "\n" "--no-fading-destroyed-argb\n" " Do not fade destroyed ARGB windows with WM frame. Workaround of bugs\n" " in Openbox, Fluxbox, etc.\n" "\n" "--shadow-ignore-shaped\n" " Do not paint shadows on shaped windows. (Deprecated, use\n" " --shadow-exclude \'bounding_shaped\' or\n" " --shadow-exclude \'bounding_shaped && !rounded_corners\' instead.)\n" "\n" "--detect-rounded-corners\n" " Try to detect windows with rounded corners and don't consider\n" " them shaped windows. Affects --shadow-ignore-shaped,\n" " --unredir-if-possible, and possibly others. You need to turn this\n" " on manually if you want to match against rounded_corners in\n" " conditions.\n" "\n" "--detect-client-opacity\n" " Detect _NET_WM_OPACITY on client windows, useful for window\n" " managers not passing _NET_WM_OPACITY of client windows to frame\n" " windows.\n" "\n" "--refresh-rate val\n" " Specify refresh rate of the screen. If not specified or 0, compton\n" " will try detecting this with X RandR extension.\n" "\n" "--vsync vsync-method\n" " Set VSync method. There are (up to) 5 VSync methods currently\n" " available:\n" " none = No VSync\n" #undef WARNING #ifndef CONFIG_VSYNC_DRM #define WARNING WARNING_DISABLED #else #define WARNING #endif " drm = VSync with DRM_IOCTL_WAIT_VBLANK. May only work on some\n" " (DRI-based) drivers." WARNING "\n" #undef WARNING #ifndef CONFIG_VSYNC_OPENGL #define WARNING WARNING_DISABLED #else #define WARNING #endif " opengl = Try to VSync with SGI_video_sync OpenGL extension. Only\n" " work on some drivers." WARNING"\n" " opengl-oml = Try to VSync with OML_sync_control OpenGL extension.\n" " Only work on some drivers." WARNING"\n" " opengl-swc = Try to VSync with SGI_swap_control OpenGL extension.\n" " Only work on some drivers. Works only with GLX backend." WARNING "\n" " opengl-mswc = Try to VSync with MESA_swap_control OpenGL\n" " extension. Basically the same as opengl-swc above, except the\n" " extension we use." WARNING "\n" "\n" "--vsync-aggressive\n" " Attempt to send painting request before VBlank and do XFlush()\n" " during VBlank. This switch may be lifted out at any moment.\n" "\n" "--alpha-step val\n" " X Render backend: Step for pregenerating alpha pictures. \n" " 0.01 - 1.0. Defaults to 0.03.\n" "\n" "--dbe\n" " Enable DBE painting mode, intended to use with VSync to\n" " (hopefully) eliminate tearing.\n" "\n" "--paint-on-overlay\n" " Painting on X Composite overlay window.\n" "\n" "--sw-opti\n" " Limit compton to repaint at most once every 1 / refresh_rate\n" " second to boost performance.\n" "\n" "--use-ewmh-active-win\n" " Use _NET_WM_ACTIVE_WINDOW on the root window to determine which\n" " window is focused instead of using FocusIn/Out events.\n" "\n" "--respect-prop-shadow\n" " Respect _COMPTON_SHADOW. This a prototype-level feature, which\n" " you must not rely on.\n" "\n" "--unredir-if-possible\n" " Unredirect all windows if a full-screen opaque window is\n" " detected, to maximize performance for full-screen windows.\n" "\n" "--unredir-if-possible-delay ms\n" " Delay before unredirecting the window, in milliseconds.\n" " Defaults to 0.\n" "\n" "--unredir-if-possible-exclude condition\n" " Conditions of windows that shouldn't be considered full-screen\n" " for unredirecting screen.\n" "\n" "--focus-exclude condition\n" " Specify a list of conditions of windows that should always be\n" " considered focused.\n" "\n" "--inactive-dim-fixed\n" " Use fixed inactive dim value.\n" "\n" "--detect-transient\n" " Use WM_TRANSIENT_FOR to group windows, and consider windows in\n" " the same group focused at the same time.\n" "\n" "--detect-client-leader\n" " Use WM_CLIENT_LEADER to group windows, and consider windows in\n" " the same group focused at the same time. WM_TRANSIENT_FOR has\n" " higher priority if --detect-transient is enabled, too.\n" "\n" "--blur-background\n" " Blur background of semi-transparent / ARGB windows. Bad in\n" " performance. The switch name may change without prior\n" " notifications.\n" "\n" "--blur-background-frame\n" " Blur background of windows when the window frame is not opaque.\n" " Implies --blur-background. Bad in performance. The switch name\n" " may change.\n" "\n" "--blur-background-fixed\n" " Use fixed blur strength instead of adjusting according to window\n" " opacity.\n" "\n" "--blur-kern matrix\n" " Specify the blur convolution kernel, with the following format:\n" " WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5...\n" " The element in the center must not be included, it will be forever\n" " 1.0 or changing based on opacity, depending on whether you have\n" " --blur-background-fixed.\n" " A 7x7 Gaussian blur kernel looks like:\n" " --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'\n" " Up to 4 blur kernels may be specified, separated with semicolon, for\n" " multi-pass blur.\n" " May also be one the predefined kernels: 3x3box (default), 5x5box,\n" " 7x7box, 3x3gaussian, 5x5gaussian, 7x7gaussian, 9x9gaussian,\n" " 11x11gaussian.\n" "\n" "--blur-background-exclude condition\n" " Exclude conditions for background blur.\n" "\n" "--resize-damage integer\n" " Resize damaged region by a specific number of pixels. A positive\n" " value enlarges it while a negative one shrinks it. Useful for\n" " fixing the line corruption issues of blur. May or may not\n" " work with --glx-no-stencil. Shrinking doesn't function correctly.\n" "\n" "--invert-color-include condition\n" " Specify a list of conditions of windows that should be painted with\n" " inverted color. Resource-hogging, and is not well tested.\n" "\n" "--opacity-rule opacity:condition\n" " Specify a list of opacity rules, in the format \"PERCENT:PATTERN\",\n" " like \'50:name *= \"Firefox\"'. compton-trans is recommended over\n" " this. Note we do not distinguish 100% and unset, and we don't make\n" " any guarantee about possible conflicts with other programs that set\n" " _NET_WM_WINDOW_OPACITY on frame or client windows.\n" "\n" "--shadow-exclude-reg geometry\n" " Specify a X geometry that describes the region in which shadow\n" " should not be painted in, such as a dock window region.\n" " Use --shadow-exclude-reg \'x10+0-0\', for example, if the 10 pixels\n" " on the bottom of the screen should not have shadows painted on.\n" #undef WARNING #ifndef CONFIG_XINERAMA #define WARNING WARNING_DISABLED #else #define WARNING #endif "\n" "--xinerama-shadow-crop\n" " Crop shadow of a window fully on a particular Xinerama screen to the\n" " screen." WARNING "\n" "\n" #undef WARNING #ifndef CONFIG_VSYNC_OPENGL #define WARNING "(GLX BACKENDS DISABLED AT COMPILE TIME)" #else #define WARNING #endif "--backend backend\n" " Choose backend. Possible choices are xrender, glx, and\n" " xr_glx_hybrid" WARNING ".\n" "\n" "--glx-no-stencil\n" " GLX backend: Avoid using stencil buffer. Might cause issues\n" " when rendering transparent content. My tests show a 15% performance\n" " boost.\n" "\n" "--glx-copy-from-front\n" " GLX backend: Copy unmodified regions from front buffer instead of\n" " redrawing them all. My tests with nvidia-drivers show a 5% decrease\n" " in performance when the whole screen is modified, but a 30% increase\n" " when only 1/4 is. My tests on nouveau show terrible slowdown. Could\n" " work with --glx-swap-method but not --glx-use-copysubbuffermesa.\n" "\n" "--glx-use-copysubbuffermesa\n" " GLX backend: Use MESA_copy_sub_buffer to do partial screen update.\n" " My tests on nouveau shows a 200% performance boost when only 1/4 of\n" " the screen is updated. May break VSync and is not available on some\n" " drivers. Overrides --glx-copy-from-front.\n" "\n" "--glx-no-rebind-pixmap\n" " GLX backend: Avoid rebinding pixmap on window damage. Probably\n" " could improve performance on rapid window content changes, but is\n" " known to break things on some drivers (LLVMpipe, xf86-video-intel,\n" " etc.).\n" "\n" "--glx-swap-method undefined/copy/exchange/3/4/5/6/buffer-age\n" " GLX backend: GLX buffer swap method we assume. Could be\n" " undefined (0), copy (1), exchange (2), 3-6, or buffer-age (-1).\n" " \"undefined\" is the slowest and the safest, and the default value.\n" " 1 is fastest, but may fail on some drivers, 2-6 are gradually slower\n" " but safer (6 is still faster than 0). -1 means auto-detect using\n" " GLX_EXT_buffer_age, supported by some drivers. Useless with\n" " --glx-use-copysubbuffermesa.\n" "\n" "--glx-use-gpushader4\n" " GLX backend: Use GL_EXT_gpu_shader4 for some optimization on blur\n" " GLSL code. My tests on GTX 670 show no noticeable effect.\n" "\n" "--xrender-sync\n" " Attempt to synchronize client applications' draw calls with XSync(),\n" " used on GLX backend to ensure up-to-date window content is painted.\n" #undef WARNING #ifndef CONFIG_XSYNC #define WARNING WARNING_DISABLED #else #define WARNING #endif "\n" "--xrender-sync-fence\n" " Additionally use X Sync fence to sync clients' draw calls. Needed\n" " on nvidia-drivers with GLX backend for some users." WARNING "\n" "\n" "--glx-fshader-win shader\n" " GLX backend: Use specified GLSL fragment shader for rendering window\n" " contents.\n" "\n" "--force-win-blend\n" " Force all windows to be painted with blending. Useful if you have a\n" " --glx-fshader-win that could turn opaque pixels transparent.\n" "\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED #else #define WARNING #endif "--dbus\n" " Enable remote control via D-Bus. See the D-BUS API section in the\n" " man page for more details." WARNING "\n" "\n" "--benchmark cycles\n" " Benchmark mode. Repeatedly paint until reaching the specified cycles.\n" "\n" "--benchmark-wid window-id\n" " Specify window ID to repaint in benchmark mode. If omitted or is 0,\n" " the whole screen is repainted.\n" ; FILE *f = (ret ? stderr: stdout); fputs(usage_text, f); #undef WARNING #undef WARNING_DISABLED exit(ret); } /** * Register a window as symbol, and initialize GLX context if wanted. */ static bool register_cm(session_t *ps) { assert(!ps->reg_win); ps->reg_win = XCreateSimpleWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0, None, None); if (!ps->reg_win) { printf_errf("(): Failed to create window."); return false; } // Unredirect the window if it's redirected, just in case if (ps->redirected) XCompositeUnredirectWindow(ps->dpy, ps->reg_win, CompositeRedirectManual); { XClassHint *h = XAllocClassHint(); if (h) { h->res_name = "compton"; h->res_class = "xcompmgr"; } Xutf8SetWMProperties(ps->dpy, ps->reg_win, "xcompmgr", "xcompmgr", NULL, 0, NULL, NULL, h); cxfree(h); } // Set _NET_WM_PID { long pid = getpid(); if (!XChangeProperty(ps->dpy, ps->reg_win, get_atom(ps, "_NET_WM_PID"), XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &pid, 1)) { printf_errf("(): Failed to set _NET_WM_PID."); } } // Set COMPTON_VERSION if (!wid_set_text_prop(ps, ps->reg_win, get_atom(ps, "COMPTON_VERSION"), COMPTON_VERSION)) { printf_errf("(): Failed to set COMPTON_VERSION."); } // Acquire X Selection _NET_WM_CM_S? if (!ps->o.no_x_selection) { unsigned len = strlen(REGISTER_PROP) + 2; int s = ps->scr; while (s >= 10) { ++len; s /= 10; } char *buf = malloc(len); snprintf(buf, len, REGISTER_PROP "%d", ps->scr); buf[len - 1] = '\0'; XSetSelectionOwner(ps->dpy, get_atom(ps, buf), ps->reg_win, 0); free(buf); } return true; } /** * Reopen streams for logging. */ static bool ostream_reopen(session_t *ps, const char *path) { if (!path) path = ps->o.logpath; if (!path) path = "/dev/null"; bool success = freopen(path, "a", stdout); success = freopen(path, "a", stderr) && success; if (!success) printf_errfq(1, "(%s): freopen() failed.", path); return success; } /** * Fork program to background and disable all I/O streams. */ static inline bool fork_after(session_t *ps) { if (getppid() == 1) return true; #ifdef CONFIG_VSYNC_OPENGL // GLX context must be released and reattached on fork if (glx_has_context(ps) && !glXMakeCurrent(ps->dpy, None, NULL)) { printf_errf("(): Failed to detach GLx context."); return false; } #endif int pid = fork(); if (-1 == pid) { printf_errf("(): fork() failed."); return false; } if (pid > 0) _exit(0); setsid(); #ifdef CONFIG_VSYNC_OPENGL if (glx_has_context(ps) && !glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->psglx->context)) { printf_errf("(): Failed to make GLX context current."); return false; } #endif // Mainly to suppress the _FORTIFY_SOURCE warning bool success = freopen("/dev/null", "r", stdin); if (!success) { printf_errf("(): freopen() failed."); return false; } return success; } /** * Write PID to a file. */ static inline bool write_pid(session_t *ps) { if (!ps->o.write_pid_path) return true; FILE *f = fopen(ps->o.write_pid_path, "w"); if (unlikely(!f)) { printf_errf("(): Failed to write PID to \"%s\".", ps->o.write_pid_path); return false; } fprintf(f, "%ld\n", (long) getpid()); fclose(f); return true; } /** * Parse a long number. */ static inline bool parse_long(const char *s, long *dest) { const char *endptr = NULL; long val = strtol(s, (char **) &endptr, 0); if (!endptr || endptr == s) { printf_errf("(\"%s\"): Invalid number.", s); return false; } while (isspace(*endptr)) ++endptr; if (*endptr) { printf_errf("(\"%s\"): Trailing characters.", s); return false; } *dest = val; return true; } /** * Parse a floating-point number in matrix. */ static inline const char * parse_matrix_readnum(const char *src, double *dest) { char *pc = NULL; double val = strtod(src, &pc); if (!pc || pc == src) { printf_errf("(\"%s\"): No number found.", src); return src; } while (*pc && (isspace(*pc) || ',' == *pc)) ++pc; *dest = val; return pc; } /** * Parse a matrix. */ static inline XFixed * parse_matrix(session_t *ps, const char *src, const char **endptr) { int wid = 0, hei = 0; const char *pc = NULL; XFixed *matrix = NULL; // Get matrix width and height { double val = 0.0; if (src == (pc = parse_matrix_readnum(src, &val))) goto parse_matrix_err; src = pc; wid = val; if (src == (pc = parse_matrix_readnum(src, &val))) goto parse_matrix_err; src = pc; hei = val; } // Validate matrix width and height if (wid <= 0 || hei <= 0) { printf_errf("(): Invalid matrix width/height."); goto parse_matrix_err; } if (!(wid % 2 && hei % 2)) { printf_errf("(): Width/height not odd."); goto parse_matrix_err; } if (wid > 16 || hei > 16) { printf_errf("(): Matrix width/height too large."); goto parse_matrix_err; } // Allocate memory matrix = calloc(wid * hei + 2, sizeof(XFixed)); if (!matrix) { printf_errf("(): Failed to allocate memory for matrix."); goto parse_matrix_err; } // Read elements { int skip = hei / 2 * wid + wid / 2; bool hasneg = false; for (int i = 0; i < wid * hei; ++i) { // Ignore the center element if (i == skip) { matrix[2 + i] = XDoubleToFixed(0); continue; } double val = 0; if (src == (pc = parse_matrix_readnum(src, &val))) goto parse_matrix_err; src = pc; if (val < 0) hasneg = true; matrix[2 + i] = XDoubleToFixed(val); } if (BKEND_XRENDER == ps->o.backend && hasneg) printf_errf("(): A convolution kernel with negative values " "may not work properly under X Render backend."); } // Detect trailing characters for ( ;*pc && ';' != *pc; ++pc) if (!isspace(*pc) && ',' != *pc) { printf_errf("(): Trailing characters in matrix string."); goto parse_matrix_err; } // Jump over spaces after ';' if (';' == *pc) { ++pc; while (*pc && isspace(*pc)) ++pc; } // Require an end of string if endptr is not provided, otherwise // copy end pointer to endptr if (endptr) *endptr = pc; else if (*pc) { printf_errf("(): Only one matrix expected."); goto parse_matrix_err; } // Fill in width and height matrix[0] = XDoubleToFixed(wid); matrix[1] = XDoubleToFixed(hei); return matrix; parse_matrix_err: free(matrix); return NULL; } /** * Parse a convolution kernel. */ static inline XFixed * parse_conv_kern(session_t *ps, const char *src, const char **endptr) { return parse_matrix(ps, src, endptr); } /** * Parse a list of convolution kernels. */ static bool parse_conv_kern_lst(session_t *ps, const char *src, XFixed **dest, int max) { static const struct { const char *name; const char *kern_str; } CONV_KERN_PREDEF[] = { { "3x3box", "3,3,1,1,1,1,1,1,1,1," }, { "5x5box", "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," }, { "7x7box", "7,7,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1," }, { "3x3gaussian", "3,3,0.243117,0.493069,0.243117,0.493069,0.493069,0.243117,0.493069,0.243117," }, { "5x5gaussian", "5,5,0.003493,0.029143,0.059106,0.029143,0.003493,0.029143,0.243117,0.493069,0.243117,0.029143,0.059106,0.493069,0.493069,0.059106,0.029143,0.243117,0.493069,0.243117,0.029143,0.003493,0.029143,0.059106,0.029143,0.003493," }, { "7x7gaussian", "7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,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.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003," }, { "9x9gaussian", "9,9,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0.000000,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0.000001,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000006,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000012,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000012,0.000006,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000001,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0.000000," }, { "11x11gaussian", "11,11,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0.000000,0.000000,0.000000,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0.000000,0.000000,0.000001,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000000,0.000000,0.000006,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000000,0.000000,0.000012,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000012,0.000000,0.000000,0.000006,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000000,0.000000,0.000001,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000000,0.000000,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0.000000,0.000000,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000," }, }; for (int i = 0; i < sizeof(CONV_KERN_PREDEF) / sizeof(CONV_KERN_PREDEF[0]); ++i) if (!strcmp(CONV_KERN_PREDEF[i].name, src)) return parse_conv_kern_lst(ps, CONV_KERN_PREDEF[i].kern_str, dest, max); int i = 0; const char *pc = src; // Free old kernels for (i = 0; i < max; ++i) { free(dest[i]); dest[i] = NULL; } // Continue parsing until the end of source string i = 0; while (pc && *pc && i < max - 1) { if (!(dest[i++] = parse_conv_kern(ps, pc, &pc))) return false; } if (*pc) { printf_errf("(): Too many blur kernels!"); return false; } return true; } /** * Parse a X geometry. */ static inline bool parse_geometry(session_t *ps, const char *src, geometry_t *dest) { geometry_t geom = { .wid = -1, .hei = -1, .x = -1, .y = -1 }; long val = 0L; char *endptr = NULL; #define T_STRIPSPACE() do { \ while (*src && isspace(*src)) ++src; \ if (!*src) goto parse_geometry_end; \ } while(0) T_STRIPSPACE(); // Parse width // Must be base 10, because "0x0..." may appear if (!('+' == *src || '-' == *src)) { val = strtol(src, &endptr, 10); if (endptr && src != endptr) { geom.wid = val; assert(geom.wid >= 0); src = endptr; } T_STRIPSPACE(); } // Parse height if ('x' == *src) { ++src; val = strtol(src, &endptr, 10); if (endptr && src != endptr) { geom.hei = val; if (geom.hei < 0) { printf_errf("(\"%s\"): Invalid height.", src); return false; } src = endptr; } T_STRIPSPACE(); } // Parse x if ('+' == *src || '-' == *src) { val = strtol(src, &endptr, 10); if (endptr && src != endptr) { geom.x = val; if ('-' == *src && geom.x <= 0) geom.x -= 2; src = endptr; } T_STRIPSPACE(); } // Parse y if ('+' == *src || '-' == *src) { val = strtol(src, &endptr, 10); if (endptr && src != endptr) { geom.y = val; if ('-' == *src && geom.y <= 0) geom.y -= 2; src = endptr; } T_STRIPSPACE(); } if (*src) { printf_errf("(\"%s\"): Trailing characters.", src); return false; } parse_geometry_end: *dest = geom; return true; } /** * Parse a list of opacity rules. */ static inline bool parse_rule_opacity(session_t *ps, const char *src) { #ifdef CONFIG_C2 // Find opacity value char *endptr = NULL; long val = strtol(src, &endptr, 0); if (!endptr || endptr == src) { printf_errf("(\"%s\"): No opacity specified?", src); return false; } if (val > 100 || val < 0) { printf_errf("(\"%s\"): Opacity %ld invalid.", src, val); return false; } // Skip over spaces while (*endptr && isspace(*endptr)) ++endptr; if (':' != *endptr) { printf_errf("(\"%s\"): Opacity terminator not found.", src); return false; } ++endptr; // Parse pattern // I hope 1-100 is acceptable for (void *) return c2_parsed(ps, &ps->o.opacity_rules, endptr, (void *) val); #else printf_errf("(\"%s\"): Condition support not compiled in.", src); return false; #endif } #ifdef CONFIG_LIBCONFIG /** * Get a file stream of the configuration file to read. * * Follows the XDG specification to search for the configuration file. */ static FILE * open_config_file(char *cpath, char **ppath) { const static char *config_filename = "/compton.conf"; const static char *config_filename_legacy = "/.compton.conf"; const static char *config_home_suffix = "/.config"; const static char *config_system_dir = "/etc/xdg"; char *dir = NULL, *home = NULL; char *path = cpath; FILE *f = NULL; if (path) { f = fopen(path, "r"); if (f && ppath) *ppath = path; return f; } // Check user configuration file in $XDG_CONFIG_HOME firstly if (!((dir = getenv("XDG_CONFIG_HOME")) && strlen(dir))) { if (!((home = getenv("HOME")) && strlen(home))) return NULL; path = mstrjoin3(home, config_home_suffix, config_filename); } else path = mstrjoin(dir, config_filename); f = fopen(path, "r"); if (f && ppath) *ppath = path; else free(path); if (f) return f; // Then check user configuration file in $HOME if ((home = getenv("HOME")) && strlen(home)) { path = mstrjoin(home, config_filename_legacy); f = fopen(path, "r"); if (f && ppath) *ppath = path; else free(path); if (f) return f; } // Check system configuration file in $XDG_CONFIG_DIRS at last if ((dir = getenv("XDG_CONFIG_DIRS")) && strlen(dir)) { char *part = strtok(dir, ":"); while (part) { path = mstrjoin(part, config_filename); f = fopen(path, "r"); if (f && ppath) *ppath = path; else free(path); if (f) return f; part = strtok(NULL, ":"); } } else { path = mstrjoin(config_system_dir, config_filename); f = fopen(path, "r"); if (f && ppath) *ppath = path; else free(path); if (f) return f; } return NULL; } /** * Parse a condition list in configuration file. */ static inline void parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst, const char *name) { config_setting_t *setting = config_lookup(pcfg, name); if (setting) { // Parse an array of options if (config_setting_is_array(setting)) { int i = config_setting_length(setting); while (i--) condlst_add(ps, pcondlst, config_setting_get_string_elem(setting, i)); } // Treat it as a single pattern if it's a string else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { condlst_add(ps, pcondlst, config_setting_get_string(setting)); } } } /** * Parse an opacity rule list in configuration file. */ static inline void parse_cfg_condlst_opct(session_t *ps, const config_t *pcfg, const char *name) { config_setting_t *setting = config_lookup(pcfg, name); if (setting) { // Parse an array of options if (config_setting_is_array(setting)) { int i = config_setting_length(setting); while (i--) if (!parse_rule_opacity(ps, config_setting_get_string_elem(setting, i))) exit(1); } // Treat it as a single pattern if it's a string else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { parse_rule_opacity(ps, config_setting_get_string(setting)); } } } /** * Parse a configuration file from default location. */ static void parse_config(session_t *ps, struct options_tmp *pcfgtmp) { char *path = NULL; FILE *f; config_t cfg; int ival = 0; double dval = 0.0; // libconfig manages string memory itself, so no need to manually free // anything const char *sval = NULL; f = open_config_file(ps->o.config_file, &path); if (!f) { if (ps->o.config_file) { printf_errfq(1, "(): Failed to read configuration file \"%s\".", ps->o.config_file); free(ps->o.config_file); ps->o.config_file = NULL; } return; } config_init(&cfg); #ifndef CONFIG_LIBCONFIG_LEGACY { // dirname() could modify the original string, thus we must pass a // copy char *path2 = mstrcpy(path); char *parent = dirname(path2); if (parent) config_set_include_dir(&cfg, parent); free(path2); } #endif { int read_result = config_read(&cfg, f); fclose(f); f = NULL; if (CONFIG_FALSE == read_result) { printf("Error when reading configuration file \"%s\", line %d: %s\n", path, config_error_line(&cfg), config_error_text(&cfg)); config_destroy(&cfg); free(path); return; } } config_set_auto_convert(&cfg, 1); if (path != ps->o.config_file) { free(ps->o.config_file); ps->o.config_file = path; } // Get options from the configuration file. We don't do range checking // right now. It will be done later // -D (fade_delta) if (lcfg_lookup_int(&cfg, "fade-delta", &ival)) ps->o.fade_delta = ival; // -I (fade_in_step) if (config_lookup_float(&cfg, "fade-in-step", &dval)) ps->o.fade_in_step = normalize_d(dval) * OPAQUE; // -O (fade_out_step) if (config_lookup_float(&cfg, "fade-out-step", &dval)) ps->o.fade_out_step = normalize_d(dval) * OPAQUE; // -r (shadow_radius) lcfg_lookup_int(&cfg, "shadow-radius", &ps->o.shadow_radius); // -o (shadow_opacity) config_lookup_float(&cfg, "shadow-opacity", &ps->o.shadow_opacity); // -l (shadow_offset_x) lcfg_lookup_int(&cfg, "shadow-offset-x", &ps->o.shadow_offset_x); // -t (shadow_offset_y) lcfg_lookup_int(&cfg, "shadow-offset-y", &ps->o.shadow_offset_y); // -i (inactive_opacity) if (config_lookup_float(&cfg, "inactive-opacity", &dval)) ps->o.inactive_opacity = normalize_d(dval) * OPAQUE; // --active_opacity if (config_lookup_float(&cfg, "active-opacity", &dval)) ps->o.active_opacity = normalize_d(dval) * OPAQUE; // -e (frame_opacity) config_lookup_float(&cfg, "frame-opacity", &ps->o.frame_opacity); // -z (clear_shadow) lcfg_lookup_bool(&cfg, "clear-shadow", &ps->o.clear_shadow); // -c (shadow_enable) if (config_lookup_bool(&cfg, "shadow", &ival) && ival) wintype_arr_enable(ps->o.wintype_shadow); // -C (no_dock_shadow) lcfg_lookup_bool(&cfg, "no-dock-shadow", &pcfgtmp->no_dock_shadow); // -G (no_dnd_shadow) lcfg_lookup_bool(&cfg, "no-dnd-shadow", &pcfgtmp->no_dnd_shadow); // -m (menu_opacity) config_lookup_float(&cfg, "menu-opacity", &pcfgtmp->menu_opacity); // -f (fading_enable) if (config_lookup_bool(&cfg, "fading", &ival) && ival) wintype_arr_enable(ps->o.wintype_fade); // --no-fading-open-close lcfg_lookup_bool(&cfg, "no-fading-openclose", &ps->o.no_fading_openclose); // --no-fading-destroyed-argb lcfg_lookup_bool(&cfg, "no-fading-destroyed-argb", &ps->o.no_fading_destroyed_argb); // --shadow-red config_lookup_float(&cfg, "shadow-red", &ps->o.shadow_red); // --shadow-green config_lookup_float(&cfg, "shadow-green", &ps->o.shadow_green); // --shadow-blue config_lookup_float(&cfg, "shadow-blue", &ps->o.shadow_blue); // --shadow-exclude-reg if (config_lookup_string(&cfg, "shadow-exclude-reg", &sval) && !parse_geometry(ps, sval, &ps->o.shadow_exclude_reg_geom)) exit(1); // --inactive-opacity-override lcfg_lookup_bool(&cfg, "inactive-opacity-override", &ps->o.inactive_opacity_override); // --inactive-dim config_lookup_float(&cfg, "inactive-dim", &ps->o.inactive_dim); // --mark-wmwin-focused lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &ps->o.mark_wmwin_focused); // --mark-ovredir-focused lcfg_lookup_bool(&cfg, "mark-ovredir-focused", &ps->o.mark_ovredir_focused); // --shadow-ignore-shaped lcfg_lookup_bool(&cfg, "shadow-ignore-shaped", &ps->o.shadow_ignore_shaped); // --detect-rounded-corners lcfg_lookup_bool(&cfg, "detect-rounded-corners", &ps->o.detect_rounded_corners); // --xinerama-shadow-crop lcfg_lookup_bool(&cfg, "xinerama-shadow-crop", &ps->o.xinerama_shadow_crop); // --detect-client-opacity lcfg_lookup_bool(&cfg, "detect-client-opacity", &ps->o.detect_client_opacity); // --refresh-rate lcfg_lookup_int(&cfg, "refresh-rate", &ps->o.refresh_rate); // --vsync if (config_lookup_string(&cfg, "vsync", &sval) && !parse_vsync(ps, sval)) exit(1); // --backend if (config_lookup_string(&cfg, "backend", &sval) && !parse_backend(ps, sval)) exit(1); // --alpha-step config_lookup_float(&cfg, "alpha-step", &ps->o.alpha_step); // --dbe lcfg_lookup_bool(&cfg, "dbe", &ps->o.dbe); // --paint-on-overlay lcfg_lookup_bool(&cfg, "paint-on-overlay", &ps->o.paint_on_overlay); // --sw-opti lcfg_lookup_bool(&cfg, "sw-opti", &ps->o.sw_opti); // --use-ewmh-active-win lcfg_lookup_bool(&cfg, "use-ewmh-active-win", &ps->o.use_ewmh_active_win); // --unredir-if-possible lcfg_lookup_bool(&cfg, "unredir-if-possible", &ps->o.unredir_if_possible); // --unredir-if-possible-delay if (lcfg_lookup_int(&cfg, "unredir-if-possible-delay", &ival)) ps->o.unredir_if_possible_delay = ival; // --inactive-dim-fixed lcfg_lookup_bool(&cfg, "inactive-dim-fixed", &ps->o.inactive_dim_fixed); // --detect-transient lcfg_lookup_bool(&cfg, "detect-transient", &ps->o.detect_transient); // --detect-client-leader lcfg_lookup_bool(&cfg, "detect-client-leader", &ps->o.detect_client_leader); // --shadow-exclude parse_cfg_condlst(ps, &cfg, &ps->o.shadow_blacklist, "shadow-exclude"); // --fade-exclude parse_cfg_condlst(ps, &cfg, &ps->o.fade_blacklist, "fade-exclude"); // --focus-exclude parse_cfg_condlst(ps, &cfg, &ps->o.focus_blacklist, "focus-exclude"); // --invert-color-include parse_cfg_condlst(ps, &cfg, &ps->o.invert_color_list, "invert-color-include"); // --blur-background-exclude parse_cfg_condlst(ps, &cfg, &ps->o.blur_background_blacklist, "blur-background-exclude"); // --opacity-rule parse_cfg_condlst_opct(ps, &cfg, "opacity-rule"); // --unredir-if-possible-exclude parse_cfg_condlst(ps, &cfg, &ps->o.unredir_if_possible_blacklist, "unredir-if-possible-exclude"); // --blur-background lcfg_lookup_bool(&cfg, "blur-background", &ps->o.blur_background); // --blur-background-frame lcfg_lookup_bool(&cfg, "blur-background-frame", &ps->o.blur_background_frame); // --blur-background-fixed lcfg_lookup_bool(&cfg, "blur-background-fixed", &ps->o.blur_background_fixed); // --blur-kern if (config_lookup_string(&cfg, "blur-kern", &sval) && !parse_conv_kern_lst(ps, sval, ps->o.blur_kerns, MAX_BLUR_PASS)) exit(1); // --resize-damage lcfg_lookup_int(&cfg, "resize-damage", &ps->o.resize_damage); // --glx-no-stencil lcfg_lookup_bool(&cfg, "glx-no-stencil", &ps->o.glx_no_stencil); // --glx-copy-from-front lcfg_lookup_bool(&cfg, "glx-copy-from-front", &ps->o.glx_copy_from_front); // --glx-use-copysubbuffermesa lcfg_lookup_bool(&cfg, "glx-use-copysubbuffermesa", &ps->o.glx_use_copysubbuffermesa); // --glx-no-rebind-pixmap lcfg_lookup_bool(&cfg, "glx-no-rebind-pixmap", &ps->o.glx_no_rebind_pixmap); // --glx-swap-method if (config_lookup_string(&cfg, "glx-swap-method", &sval) && !parse_glx_swap_method(ps, sval)) exit(1); // --glx-use-gpushader4 lcfg_lookup_bool(&cfg, "glx-use-gpushader4", &ps->o.glx_use_gpushader4); // --xrender-sync lcfg_lookup_bool(&cfg, "xrender-sync", &ps->o.xrender_sync); // --xrender-sync-fence lcfg_lookup_bool(&cfg, "xrender-sync-fence", &ps->o.xrender_sync_fence); // Wintype settings { wintype_t i; for (i = 0; i < NUM_WINTYPES; ++i) { char *str = mstrjoin("wintypes.", WINTYPES[i]); config_setting_t *setting = config_lookup(&cfg, str); free(str); if (setting) { if (config_setting_lookup_bool(setting, "shadow", &ival)) ps->o.wintype_shadow[i] = (bool) ival; if (config_setting_lookup_bool(setting, "fade", &ival)) ps->o.wintype_fade[i] = (bool) ival; if (config_setting_lookup_bool(setting, "focus", &ival)) ps->o.wintype_focus[i] = (bool) ival; config_setting_lookup_float(setting, "opacity", &ps->o.wintype_opacity[i]); } } } config_destroy(&cfg); } #endif /** * Process arguments and configuration files. */ static void get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { const static char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:hscnfFCaSzGb"; const static struct option longopts[] = { { "help", no_argument, NULL, 'h' }, { "config", required_argument, NULL, 256 }, { "shadow-radius", required_argument, NULL, 'r' }, { "shadow-opacity", required_argument, NULL, 'o' }, { "shadow-offset-x", required_argument, NULL, 'l' }, { "shadow-offset-y", required_argument, NULL, 't' }, { "fade-in-step", required_argument, NULL, 'I' }, { "fade-out-step", required_argument, NULL, 'O' }, { "fade-delta", required_argument, NULL, 'D' }, { "menu-opacity", required_argument, NULL, 'm' }, { "shadow", no_argument, NULL, 'c' }, { "no-dock-shadow", no_argument, NULL, 'C' }, { "clear-shadow", no_argument, NULL, 'z' }, { "fading", no_argument, NULL, 'f' }, { "inactive-opacity", required_argument, NULL, 'i' }, { "frame-opacity", required_argument, NULL, 'e' }, { "daemon", no_argument, NULL, 'b' }, { "no-dnd-shadow", no_argument, NULL, 'G' }, { "shadow-red", required_argument, NULL, 257 }, { "shadow-green", required_argument, NULL, 258 }, { "shadow-blue", required_argument, NULL, 259 }, { "inactive-opacity-override", no_argument, NULL, 260 }, { "inactive-dim", required_argument, NULL, 261 }, { "mark-wmwin-focused", no_argument, NULL, 262 }, { "shadow-exclude", required_argument, NULL, 263 }, { "mark-ovredir-focused", no_argument, NULL, 264 }, { "no-fading-openclose", no_argument, NULL, 265 }, { "shadow-ignore-shaped", no_argument, NULL, 266 }, { "detect-rounded-corners", no_argument, NULL, 267 }, { "detect-client-opacity", no_argument, NULL, 268 }, { "refresh-rate", required_argument, NULL, 269 }, { "vsync", required_argument, NULL, 270 }, { "alpha-step", required_argument, NULL, 271 }, { "dbe", no_argument, NULL, 272 }, { "paint-on-overlay", no_argument, NULL, 273 }, { "sw-opti", no_argument, NULL, 274 }, { "vsync-aggressive", no_argument, NULL, 275 }, { "use-ewmh-active-win", no_argument, NULL, 276 }, { "respect-prop-shadow", no_argument, NULL, 277 }, { "unredir-if-possible", no_argument, NULL, 278 }, { "focus-exclude", required_argument, NULL, 279 }, { "inactive-dim-fixed", no_argument, NULL, 280 }, { "detect-transient", no_argument, NULL, 281 }, { "detect-client-leader", no_argument, NULL, 282 }, { "blur-background", no_argument, NULL, 283 }, { "blur-background-frame", no_argument, NULL, 284 }, { "blur-background-fixed", no_argument, NULL, 285 }, { "dbus", no_argument, NULL, 286 }, { "logpath", required_argument, NULL, 287 }, { "invert-color-include", required_argument, NULL, 288 }, { "opengl", no_argument, NULL, 289 }, { "backend", required_argument, NULL, 290 }, { "glx-no-stencil", no_argument, NULL, 291 }, { "glx-copy-from-front", no_argument, NULL, 292 }, { "benchmark", required_argument, NULL, 293 }, { "benchmark-wid", required_argument, NULL, 294 }, { "glx-use-copysubbuffermesa", no_argument, NULL, 295 }, { "blur-background-exclude", required_argument, NULL, 296 }, { "active-opacity", required_argument, NULL, 297 }, { "glx-no-rebind-pixmap", no_argument, NULL, 298 }, { "glx-swap-method", required_argument, NULL, 299 }, { "fade-exclude", required_argument, NULL, 300 }, { "blur-kern", required_argument, NULL, 301 }, { "resize-damage", required_argument, NULL, 302 }, { "glx-use-gpushader4", no_argument, NULL, 303 }, { "opacity-rule", required_argument, NULL, 304 }, { "shadow-exclude-reg", required_argument, NULL, 305 }, { "paint-exclude", required_argument, NULL, 306 }, { "xinerama-shadow-crop", no_argument, NULL, 307 }, { "unredir-if-possible-exclude", required_argument, NULL, 308 }, { "unredir-if-possible-delay", required_argument, NULL, 309 }, { "write-pid-path", required_argument, NULL, 310 }, { "vsync-use-glfinish", no_argument, NULL, 311 }, { "xrender-sync", no_argument, NULL, 312 }, { "xrender-sync-fence", no_argument, NULL, 313 }, { "show-all-xerrors", no_argument, NULL, 314 }, { "no-fading-destroyed-argb", no_argument, NULL, 315 }, { "force-win-blend", no_argument, NULL, 316 }, { "glx-fshader-win", required_argument, NULL, 317 }, { "version", no_argument, NULL, 318 }, { "no-x-selection", no_argument, NULL, 319 }, { "no-name-pixmap", no_argument, NULL, 320 }, { "reredir-on-root-change", no_argument, NULL, 731 }, { "glx-reinit-on-root-change", no_argument, NULL, 732 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; int o = 0, longopt_idx = -1, i = 0; if (first_pass) { // Pre-parse the commandline arguments to check for --config and invalid // switches // Must reset optind to 0 here in case we reread the commandline // arguments optind = 1; while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { if (256 == o) ps->o.config_file = mstrcpy(optarg); else if ('d' == o) ps->o.display = mstrcpy(optarg); else if ('S' == o) ps->o.synchronize = true; else if (314 == o) ps->o.show_all_xerrors = true; else if (318 == o) { printf("%s\n", COMPTON_VERSION); exit(0); } else if (320 == o) ps->o.no_name_pixmap = true; else if ('?' == o || ':' == o) usage(1); } // Check for abundant positional arguments if (optind < argc) printf_errfq(1, "(): compton doesn't accept positional arguments."); return; } struct options_tmp cfgtmp = { .no_dock_shadow = false, .no_dnd_shadow = false, .menu_opacity = 1.0, }; bool shadow_enable = false, fading_enable = false; char *lc_numeric_old = mstrcpy(setlocale(LC_NUMERIC, NULL)); for (i = 0; i < NUM_WINTYPES; ++i) { ps->o.wintype_fade[i] = false; ps->o.wintype_shadow[i] = false; ps->o.wintype_opacity[i] = 1.0; } // Enforce LC_NUMERIC locale "C" here to make sure dots are recognized // instead of commas in atof(). setlocale(LC_NUMERIC, "C"); #ifdef CONFIG_LIBCONFIG parse_config(ps, &cfgtmp); #endif // Parse commandline arguments. Range checking will be done later. optind = 1; while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { long val = 0; switch (o) { #define P_CASEBOOL(idx, option) case idx: ps->o.option = true; break #define P_CASELONG(idx, option) \ case idx: \ if (!parse_long(optarg, &val)) exit(1); \ ps->o.option = val; \ break // Short options case 'h': usage(0); break; case 'd': case 'S': case 314: case 318: case 320: break; P_CASELONG('D', fade_delta); case 'I': ps->o.fade_in_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'O': ps->o.fade_out_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'c': shadow_enable = true; break; case 'C': cfgtmp.no_dock_shadow = true; break; case 'G': cfgtmp.no_dnd_shadow = true; break; case 'm': cfgtmp.menu_opacity = atof(optarg); break; case 'f': case 'F': fading_enable = true; break; P_CASELONG('r', shadow_radius); case 'o': ps->o.shadow_opacity = atof(optarg); break; P_CASELONG('l', shadow_offset_x); P_CASELONG('t', shadow_offset_y); case 'i': ps->o.inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); break; case 'e': ps->o.frame_opacity = atof(optarg); break; P_CASEBOOL('z', clear_shadow); case 'n': case 'a': case 's': printf_errfq(1, "(): -n, -a, and -s have been removed."); break; P_CASEBOOL('b', fork_after_register); // Long options case 256: // --config break; case 257: // --shadow-red ps->o.shadow_red = atof(optarg); break; case 258: // --shadow-green ps->o.shadow_green = atof(optarg); break; case 259: // --shadow-blue ps->o.shadow_blue = atof(optarg); break; P_CASEBOOL(260, inactive_opacity_override); case 261: // --inactive-dim ps->o.inactive_dim = atof(optarg); break; P_CASEBOOL(262, mark_wmwin_focused); case 263: // --shadow-exclude condlst_add(ps, &ps->o.shadow_blacklist, optarg); break; P_CASEBOOL(264, mark_ovredir_focused); P_CASEBOOL(265, no_fading_openclose); P_CASEBOOL(266, shadow_ignore_shaped); P_CASEBOOL(267, detect_rounded_corners); P_CASEBOOL(268, detect_client_opacity); P_CASELONG(269, refresh_rate); case 270: // --vsync if (!parse_vsync(ps, optarg)) exit(1); break; case 271: // --alpha-step ps->o.alpha_step = atof(optarg); break; P_CASEBOOL(272, dbe); P_CASEBOOL(273, paint_on_overlay); P_CASEBOOL(274, sw_opti); P_CASEBOOL(275, vsync_aggressive); P_CASEBOOL(276, use_ewmh_active_win); P_CASEBOOL(277, respect_prop_shadow); P_CASEBOOL(278, unredir_if_possible); case 279: // --focus-exclude condlst_add(ps, &ps->o.focus_blacklist, optarg); break; P_CASEBOOL(280, inactive_dim_fixed); P_CASEBOOL(281, detect_transient); P_CASEBOOL(282, detect_client_leader); P_CASEBOOL(283, blur_background); P_CASEBOOL(284, blur_background_frame); P_CASEBOOL(285, blur_background_fixed); P_CASEBOOL(286, dbus); case 287: // --logpath ps->o.logpath = mstrcpy(optarg); break; case 288: // --invert-color-include condlst_add(ps, &ps->o.invert_color_list, optarg); break; case 289: // --opengl ps->o.backend = BKEND_GLX; break; case 290: // --backend if (!parse_backend(ps, optarg)) exit(1); break; P_CASEBOOL(291, glx_no_stencil); P_CASEBOOL(292, glx_copy_from_front); P_CASELONG(293, benchmark); case 294: // --benchmark-wid ps->o.benchmark_wid = strtol(optarg, NULL, 0); break; P_CASEBOOL(295, glx_use_copysubbuffermesa); case 296: // --blur-background-exclude condlst_add(ps, &ps->o.blur_background_blacklist, optarg); break; case 297: // --active-opacity ps->o.active_opacity = (normalize_d(atof(optarg)) * OPAQUE); break; P_CASEBOOL(298, glx_no_rebind_pixmap); case 299: // --glx-swap-method if (!parse_glx_swap_method(ps, optarg)) exit(1); break; case 300: // --fade-exclude condlst_add(ps, &ps->o.fade_blacklist, optarg); break; case 301: // --blur-kern if (!parse_conv_kern_lst(ps, optarg, ps->o.blur_kerns, MAX_BLUR_PASS)) exit(1); break; P_CASELONG(302, resize_damage); P_CASEBOOL(303, glx_use_gpushader4); case 304: // --opacity-rule if (!parse_rule_opacity(ps, optarg)) exit(1); break; case 305: // --shadow-exclude-reg if (!parse_geometry(ps, optarg, &ps->o.shadow_exclude_reg_geom)) exit(1); break; case 306: // --paint-exclude condlst_add(ps, &ps->o.paint_blacklist, optarg); break; P_CASEBOOL(307, xinerama_shadow_crop); case 308: // --unredir-if-possible-exclude condlst_add(ps, &ps->o.unredir_if_possible_blacklist, optarg); break; P_CASELONG(309, unredir_if_possible_delay); case 310: // --write-pid-path ps->o.write_pid_path = mstrcpy(optarg); break; P_CASEBOOL(311, vsync_use_glfinish); P_CASEBOOL(312, xrender_sync); P_CASEBOOL(313, xrender_sync_fence); P_CASEBOOL(315, no_fading_destroyed_argb); P_CASEBOOL(316, force_win_blend); case 317: ps->o.glx_fshader_win_str = mstrcpy(optarg); break; P_CASEBOOL(319, no_x_selection); P_CASEBOOL(731, reredir_on_root_change); P_CASEBOOL(732, glx_reinit_on_root_change); default: usage(1); break; #undef P_CASEBOOL } } // Restore LC_NUMERIC setlocale(LC_NUMERIC, lc_numeric_old); free(lc_numeric_old); // Range checking and option assignments ps->o.fade_delta = max_i(ps->o.fade_delta, 1); ps->o.shadow_radius = max_i(ps->o.shadow_radius, 1); ps->o.shadow_red = normalize_d(ps->o.shadow_red); ps->o.shadow_green = normalize_d(ps->o.shadow_green); ps->o.shadow_blue = normalize_d(ps->o.shadow_blue); ps->o.inactive_dim = normalize_d(ps->o.inactive_dim); ps->o.frame_opacity = normalize_d(ps->o.frame_opacity); ps->o.shadow_opacity = normalize_d(ps->o.shadow_opacity); cfgtmp.menu_opacity = normalize_d(cfgtmp.menu_opacity); ps->o.refresh_rate = normalize_i_range(ps->o.refresh_rate, 0, 300); ps->o.alpha_step = normalize_d_range(ps->o.alpha_step, 0.01, 1.0); if (OPAQUE == ps->o.inactive_opacity) { ps->o.inactive_opacity = 0; } if (OPAQUE == ps->o.active_opacity) { ps->o.active_opacity = 0; } if (shadow_enable) wintype_arr_enable(ps->o.wintype_shadow); ps->o.wintype_shadow[WINTYPE_DESKTOP] = false; if (cfgtmp.no_dock_shadow) ps->o.wintype_shadow[WINTYPE_DOCK] = false; if (cfgtmp.no_dnd_shadow) ps->o.wintype_shadow[WINTYPE_DND] = false; if (fading_enable) wintype_arr_enable(ps->o.wintype_fade); if (1.0 != cfgtmp.menu_opacity) { ps->o.wintype_opacity[WINTYPE_DROPDOWN_MENU] = cfgtmp.menu_opacity; ps->o.wintype_opacity[WINTYPE_POPUP_MENU] = cfgtmp.menu_opacity; } // --blur-background-frame implies --blur-background if (ps->o.blur_background_frame) ps->o.blur_background = true; if (ps->o.xrender_sync_fence) ps->o.xrender_sync = true; // Other variables determined by options // Determine whether we need to track focus changes if (ps->o.inactive_opacity || ps->o.active_opacity || ps->o.inactive_dim) { ps->o.track_focus = true; } // Determine whether we track window grouping if (ps->o.detect_transient || ps->o.detect_client_leader) { ps->o.track_leader = true; } // Fill default blur kernel if (ps->o.blur_background && !ps->o.blur_kerns[0]) { // Convolution filter parameter (box blur) // gaussian or binomial filters are definitely superior, yet looks // like they aren't supported as of xorg-server-1.13.0 const static XFixed convolution_blur[] = { // Must convert to XFixed with XDoubleToFixed() // Matrix size XDoubleToFixed(3), XDoubleToFixed(3), // Matrix XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), }; ps->o.blur_kerns[0] = malloc(sizeof(convolution_blur)); if (!ps->o.blur_kerns[0]) { printf_errf("(): Failed to allocate memory for convolution kernel."); exit(1); } memcpy(ps->o.blur_kerns[0], &convolution_blur, sizeof(convolution_blur)); } rebuild_shadow_exclude_reg(ps); if (ps->o.resize_damage < 0) printf_errf("(): Negative --resize-damage does not work correctly."); } /** * Fetch all required atoms and save them to a session. */ static void init_atoms(session_t *ps) { ps->atom_opacity = get_atom(ps, "_NET_WM_WINDOW_OPACITY"); ps->atom_frame_extents = get_atom(ps, "_NET_FRAME_EXTENTS"); ps->atom_client = get_atom(ps, "WM_STATE"); ps->atom_name = XA_WM_NAME; ps->atom_name_ewmh = get_atom(ps, "_NET_WM_NAME"); ps->atom_class = XA_WM_CLASS; ps->atom_role = get_atom(ps, "WM_WINDOW_ROLE"); ps->atom_transient = XA_WM_TRANSIENT_FOR; ps->atom_client_leader = get_atom(ps, "WM_CLIENT_LEADER"); ps->atom_ewmh_active_win = get_atom(ps, "_NET_ACTIVE_WINDOW"); ps->atom_compton_shadow = get_atom(ps, "_COMPTON_SHADOW"); ps->atom_win_type = get_atom(ps, "_NET_WM_WINDOW_TYPE"); ps->atoms_wintypes[WINTYPE_UNKNOWN] = 0; ps->atoms_wintypes[WINTYPE_DESKTOP] = get_atom(ps, "_NET_WM_WINDOW_TYPE_DESKTOP"); ps->atoms_wintypes[WINTYPE_DOCK] = get_atom(ps, "_NET_WM_WINDOW_TYPE_DOCK"); ps->atoms_wintypes[WINTYPE_TOOLBAR] = get_atom(ps, "_NET_WM_WINDOW_TYPE_TOOLBAR"); ps->atoms_wintypes[WINTYPE_MENU] = get_atom(ps, "_NET_WM_WINDOW_TYPE_MENU"); ps->atoms_wintypes[WINTYPE_UTILITY] = get_atom(ps, "_NET_WM_WINDOW_TYPE_UTILITY"); ps->atoms_wintypes[WINTYPE_SPLASH] = get_atom(ps, "_NET_WM_WINDOW_TYPE_SPLASH"); ps->atoms_wintypes[WINTYPE_DIALOG] = get_atom(ps, "_NET_WM_WINDOW_TYPE_DIALOG"); ps->atoms_wintypes[WINTYPE_NORMAL] = get_atom(ps, "_NET_WM_WINDOW_TYPE_NORMAL"); ps->atoms_wintypes[WINTYPE_DROPDOWN_MENU] = get_atom(ps, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"); ps->atoms_wintypes[WINTYPE_POPUP_MENU] = get_atom(ps, "_NET_WM_WINDOW_TYPE_POPUP_MENU"); ps->atoms_wintypes[WINTYPE_TOOLTIP] = get_atom(ps, "_NET_WM_WINDOW_TYPE_TOOLTIP"); ps->atoms_wintypes[WINTYPE_NOTIFY] = get_atom(ps, "_NET_WM_WINDOW_TYPE_NOTIFICATION"); ps->atoms_wintypes[WINTYPE_COMBO] = get_atom(ps, "_NET_WM_WINDOW_TYPE_COMBO"); ps->atoms_wintypes[WINTYPE_DND] = get_atom(ps, "_NET_WM_WINDOW_TYPE_DND"); } /** * Update refresh rate info with X Randr extension. */ static void update_refresh_rate(session_t *ps) { XRRScreenConfiguration* randr_info; if (!(randr_info = XRRGetScreenInfo(ps->dpy, ps->root))) return; ps->refresh_rate = XRRConfigCurrentRate(randr_info); XRRFreeScreenConfigInfo(randr_info); if (ps->refresh_rate) ps->refresh_intv = US_PER_SEC / ps->refresh_rate; else ps->refresh_intv = 0; } /** * Initialize refresh-rated based software optimization. * * @return true for success, false otherwise */ static bool swopti_init(session_t *ps) { // Prepare refresh rate // Check if user provides one ps->refresh_rate = ps->o.refresh_rate; if (ps->refresh_rate) ps->refresh_intv = US_PER_SEC / ps->refresh_rate; // Auto-detect refresh rate otherwise if (!ps->refresh_rate && ps->randr_exists) { update_refresh_rate(ps); } // Turn off vsync_sw if we can't get the refresh rate if (!ps->refresh_rate) return false; return true; } /** * Modify a struct timeval timeout value to render at a fixed pace. * * @param ps current session * @param[in,out] ptv pointer to the timeout */ static void swopti_handle_timeout(session_t *ps, struct timeval *ptv) { if (!ptv) return; // Get the microsecond offset of the time when the we reach the timeout // I don't think a 32-bit long could overflow here. long offset = (ptv->tv_usec + get_time_timeval().tv_usec - ps->paint_tm_offset) % ps->refresh_intv; if (offset < 0) offset += ps->refresh_intv; assert(offset >= 0 && offset < ps->refresh_intv); // If the target time is sufficiently close to a refresh time, don't add // an offset, to avoid certain blocking conditions. if (offset < SWOPTI_TOLERANCE || offset > ps->refresh_intv - SWOPTI_TOLERANCE) return; // Add an offset so we wait until the next refresh after timeout ptv->tv_usec += ps->refresh_intv - offset; if (ptv->tv_usec > US_PER_SEC) { ptv->tv_usec -= US_PER_SEC; ++ptv->tv_sec; } } /** * Initialize DRM VSync. * * @return true for success, false otherwise */ static bool vsync_drm_init(session_t *ps) { #ifdef CONFIG_VSYNC_DRM // Should we always open card0? if (ps->drm_fd < 0 && (ps->drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) { printf_errf("(): Failed to open device."); return false; } if (vsync_drm_wait(ps)) return false; return true; #else printf_errf("(): Program not compiled with DRM VSync support."); return false; #endif } #ifdef CONFIG_VSYNC_DRM /** * Wait for next VSync, DRM method. * * Stolen from: https://github.com/MythTV/mythtv/blob/master/mythtv/libs/libmythtv/vsync.cpp */ static int vsync_drm_wait(session_t *ps) { int ret = -1; drm_wait_vblank_t vbl; vbl.request.type = _DRM_VBLANK_RELATIVE, vbl.request.sequence = 1; do { ret = ioctl(ps->drm_fd, DRM_IOCTL_WAIT_VBLANK, &vbl); vbl.request.type &= ~_DRM_VBLANK_RELATIVE; } while (ret && errno == EINTR); if (ret) fprintf(stderr, "vsync_drm_wait(): VBlank ioctl did not work, " "unimplemented in this drmver?\n"); return ret; } #endif /** * Initialize OpenGL VSync. * * Stolen from: http://git.tuxfamily.org/?p=ccm/cairocompmgr.git;a=commitdiff;h=efa4ceb97da501e8630ca7f12c99b1dce853c73e * Possible original source: http://www.inb.uni-luebeck.de/~boehme/xvideo_sync.html * * @return true for success, false otherwise */ static bool vsync_opengl_init(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL if (!ensure_glx_context(ps)) return false; // Get video sync functions if (!ps->psglx->glXGetVideoSyncSGI) ps->psglx->glXGetVideoSyncSGI = (f_GetVideoSync) glXGetProcAddress((const GLubyte *) "glXGetVideoSyncSGI"); if (!ps->psglx->glXWaitVideoSyncSGI) ps->psglx->glXWaitVideoSyncSGI = (f_WaitVideoSync) glXGetProcAddress((const GLubyte *) "glXWaitVideoSyncSGI"); if (!ps->psglx->glXWaitVideoSyncSGI || !ps->psglx->glXGetVideoSyncSGI) { printf_errf("(): Failed to get glXWait/GetVideoSyncSGI function."); return false; } return true; #else printf_errf("(): Program not compiled with OpenGL VSync support."); return false; #endif } static bool vsync_opengl_oml_init(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL if (!ensure_glx_context(ps)) return false; // Get video sync functions if (!ps->psglx->glXGetSyncValuesOML) ps->psglx->glXGetSyncValuesOML = (f_GetSyncValuesOML) glXGetProcAddress ((const GLubyte *) "glXGetSyncValuesOML"); if (!ps->psglx->glXWaitForMscOML) ps->psglx->glXWaitForMscOML = (f_WaitForMscOML) glXGetProcAddress ((const GLubyte *) "glXWaitForMscOML"); if (!ps->psglx->glXGetSyncValuesOML || !ps->psglx->glXWaitForMscOML) { printf_errf("(): Failed to get OML_sync_control functions."); return false; } return true; #else printf_errf("(): Program not compiled with OpenGL VSync support."); return false; #endif } static bool vsync_opengl_swc_init(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL if (!ensure_glx_context(ps)) return false; if (!bkend_use_glx(ps)) { printf_errf("(): I'm afraid glXSwapIntervalSGI wouldn't help if you are " "not using GLX backend. You could try, nonetheless."); } // Get video sync functions if (!ps->psglx->glXSwapIntervalProc) ps->psglx->glXSwapIntervalProc = (f_SwapIntervalSGI) glXGetProcAddress ((const GLubyte *) "glXSwapIntervalSGI"); if (!ps->psglx->glXSwapIntervalProc) { printf_errf("(): Failed to get SGI_swap_control function."); return false; } ps->psglx->glXSwapIntervalProc(1); return true; #else printf_errf("(): Program not compiled with OpenGL VSync support."); return false; #endif } static bool vsync_opengl_mswc_init(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL if (!ensure_glx_context(ps)) return false; if (!bkend_use_glx(ps)) { printf_errf("(): I'm afraid glXSwapIntervalMESA wouldn't help if you are " "not using GLX backend. You could try, nonetheless."); } // Get video sync functions if (!ps->psglx->glXSwapIntervalMESAProc) ps->psglx->glXSwapIntervalMESAProc = (f_SwapIntervalMESA) glXGetProcAddress ((const GLubyte *) "glXSwapIntervalMESA"); if (!ps->psglx->glXSwapIntervalMESAProc) { printf_errf("(): Failed to get MESA_swap_control function."); return false; } ps->psglx->glXSwapIntervalMESAProc(1); return true; #else printf_errf("(): Program not compiled with OpenGL VSync support."); return false; #endif } #ifdef CONFIG_VSYNC_OPENGL /** * Wait for next VSync, OpenGL method. */ static int vsync_opengl_wait(session_t *ps) { unsigned vblank_count = 0; ps->psglx->glXGetVideoSyncSGI(&vblank_count); ps->psglx->glXWaitVideoSyncSGI(2, (vblank_count + 1) % 2, &vblank_count); // I see some code calling glXSwapIntervalSGI(1) afterwards, is it required? return 0; } /** * Wait for next VSync, OpenGL OML method. * * https://mail.gnome.org/archives/clutter-list/2012-November/msg00031.html */ static int vsync_opengl_oml_wait(session_t *ps) { int64_t ust = 0, msc = 0, sbc = 0; ps->psglx->glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc); ps->psglx->glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2, &ust, &msc, &sbc); return 0; } static void vsync_opengl_swc_deinit(session_t *ps) { // The standard says it doesn't accept 0, but in fact it probably does if (glx_has_context(ps) && ps->psglx->glXSwapIntervalProc) ps->psglx->glXSwapIntervalProc(0); } static void vsync_opengl_mswc_deinit(session_t *ps) { if (glx_has_context(ps) && ps->psglx->glXSwapIntervalMESAProc) ps->psglx->glXSwapIntervalMESAProc(0); } #endif /** * Initialize current VSync method. */ bool vsync_init(session_t *ps) { if (ps->o.vsync && VSYNC_FUNCS_INIT[ps->o.vsync] && !VSYNC_FUNCS_INIT[ps->o.vsync](ps)) { ps->o.vsync = VSYNC_NONE; return false; } else return true; } /** * Wait for next VSync. */ static void vsync_wait(session_t *ps) { if (!ps->o.vsync) return; if (VSYNC_FUNCS_WAIT[ps->o.vsync]) VSYNC_FUNCS_WAIT[ps->o.vsync](ps); } /** * Deinitialize current VSync method. */ void vsync_deinit(session_t *ps) { if (ps->o.vsync && VSYNC_FUNCS_DEINIT[ps->o.vsync]) VSYNC_FUNCS_DEINIT[ps->o.vsync](ps); } /** * Pregenerate alpha pictures. */ static void init_alpha_picts(session_t *ps) { int i; int num = round(1.0 / ps->o.alpha_step) + 1; ps->alpha_picts = malloc(sizeof(Picture) * num); for (i = 0; i < num; ++i) { double o = i * ps->o.alpha_step; if ((1.0 - o) > ps->o.alpha_step) ps->alpha_picts[i] = solid_picture(ps, false, o, 0, 0, 0); else ps->alpha_picts[i] = None; } } /** * Initialize double buffer. */ static bool init_dbe(session_t *ps) { if (!(ps->root_dbe = XdbeAllocateBackBufferName(ps->dpy, (ps->o.paint_on_overlay ? ps->overlay: ps->root), XdbeCopied))) { printf_errf("(): Failed to create double buffer. Double buffering " "cannot work."); return false; } return true; } /** * Initialize X composite overlay window. */ static bool init_overlay(session_t *ps) { ps->overlay = XCompositeGetOverlayWindow(ps->dpy, ps->root); if (ps->overlay) { // Set window region of the overlay window, code stolen from // compiz-0.8.8 XserverRegion region = XFixesCreateRegion(ps->dpy, NULL, 0); XFixesSetWindowShapeRegion(ps->dpy, ps->overlay, ShapeBounding, 0, 0, 0); XFixesSetWindowShapeRegion(ps->dpy, ps->overlay, ShapeInput, 0, 0, region); XFixesDestroyRegion(ps->dpy, region); // Listen to Expose events on the overlay XSelectInput(ps->dpy, ps->overlay, ExposureMask); // Retrieve DamageNotify on root window if we are painting on an // overlay // root_damage = XDamageCreate(ps->dpy, root, XDamageReportNonEmpty); // Unmap overlay, firstly. But this typically does not work because // the window isn't created yet. // XUnmapWindow(ps->dpy, ps->overlay); // XFlush(ps->dpy); } else { fprintf(stderr, "Cannot get X Composite overlay window. Falling " "back to painting on root window.\n"); ps->o.paint_on_overlay = false; } #ifdef DEBUG_REDIR printf_dbgf("(): overlay = %#010lx\n", ps->overlay); #endif return ps->overlay; } /** * Query needed X Render / OpenGL filters to check for their existence. */ static bool init_filters(session_t *ps) { // Blur filter if (ps->o.blur_background || ps->o.blur_background_frame) { switch (ps->o.backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: { // Query filters XFilters *pf = XRenderQueryFilters(ps->dpy, get_tgt_window(ps)); if (pf) { for (int i = 0; i < pf->nfilter; ++i) { // Convolution filter if (!strcmp(pf->filter[i], XRFILTER_CONVOLUTION)) ps->xrfilter_convolution_exists = true; } } cxfree(pf); // Turn features off if any required filter is not present if (!ps->xrfilter_convolution_exists) { printf_errf("(): X Render convolution filter unsupported by your X server. Background blur is not possible."); return false; } break; } #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: { if (!glx_init_blur(ps)) return false; } #endif } } return true; } /** * Redirect all windows. */ static void redir_start(session_t *ps) { if (!ps->redirected) { #ifdef DEBUG_REDIR print_timestamp(ps); printf_dbgf("(): Screen redirected.\n"); #endif // Map overlay window. Done firstly according to this: // https://bugzilla.gnome.org/show_bug.cgi?id=597014 if (ps->overlay) XMapWindow(ps->dpy, ps->overlay); XCompositeRedirectSubwindows(ps->dpy, ps->root, CompositeRedirectManual); /* // Unredirect GL context window as this may have an effect on VSync: // < http://dri.freedesktop.org/wiki/CompositeSwap > XCompositeUnredirectWindow(ps->dpy, ps->reg_win, CompositeRedirectManual); if (ps->o.paint_on_overlay && ps->overlay) { XCompositeUnredirectWindow(ps->dpy, ps->overlay, CompositeRedirectManual); } */ // Must call XSync() here XSync(ps->dpy, False); ps->redirected = true; // Repaint the whole screen force_repaint(ps); } } /** * Get the poll time. */ static time_ms_t timeout_get_poll_time(session_t *ps) { const time_ms_t now = get_time_ms(); time_ms_t wait = TIME_MS_MAX; // Traverse throught the timeout linked list for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = ptmout->next) { if (ptmout->enabled) { time_ms_t newrun = timeout_get_newrun(ptmout); if (newrun <= now) { wait = 0; break; } else { time_ms_t newwait = newrun - now; if (newwait < wait) wait = newwait; } } } return wait; } /** * Insert a new timeout. */ timeout_t * timeout_insert(session_t *ps, time_ms_t interval, bool (*callback)(session_t *ps, timeout_t *ptmout), void *data) { const static timeout_t tmout_def = { .enabled = true, .data = NULL, .callback = NULL, .firstrun = 0L, .lastrun = 0L, .interval = 0L, }; const time_ms_t now = get_time_ms(); timeout_t *ptmout = malloc(sizeof(timeout_t)); if (!ptmout) printf_errfq(1, "(): Failed to allocate memory for timeout."); memcpy(ptmout, &tmout_def, sizeof(timeout_t)); ptmout->interval = interval; ptmout->firstrun = now; ptmout->lastrun = now; ptmout->data = data; ptmout->callback = callback; ptmout->next = ps->tmout_lst; ps->tmout_lst = ptmout; return ptmout; } /** * Drop a timeout. * * @return true if we have found the timeout and removed it, false * otherwise */ bool timeout_drop(session_t *ps, timeout_t *prm) { timeout_t **pplast = &ps->tmout_lst; for (timeout_t *ptmout = ps->tmout_lst; ptmout; pplast = &ptmout->next, ptmout = ptmout->next) { if (prm == ptmout) { *pplast = ptmout->next; free(ptmout); return true; } } return false; } /** * Clear all timeouts. */ static void timeout_clear(session_t *ps) { timeout_t *ptmout = ps->tmout_lst; timeout_t *next = NULL; while (ptmout) { next = ptmout->next; free(ptmout); ptmout = next; } } /** * Run timeouts. * * @return true if we have ran a timeout, false otherwise */ static bool timeout_run(session_t *ps) { const time_ms_t now = get_time_ms(); bool ret = false; timeout_t *pnext = NULL; for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = pnext) { pnext = ptmout->next; if (ptmout->enabled) { const time_ms_t max = now + (time_ms_t) (ptmout->interval * TIMEOUT_RUN_TOLERANCE); time_ms_t newrun = timeout_get_newrun(ptmout); if (newrun <= max) { ret = true; timeout_invoke(ps, ptmout); } } } return ret; } /** * Invoke a timeout. */ void timeout_invoke(session_t *ps, timeout_t *ptmout) { const time_ms_t now = get_time_ms(); ptmout->lastrun = now; // Avoid modifying the timeout structure after running timeout, to // make it possible to remove timeout in callback if (ptmout->callback) ptmout->callback(ps, ptmout); } /** * Reset a timeout to initial state. */ void timeout_reset(session_t *ps, timeout_t *ptmout) { ptmout->firstrun = ptmout->lastrun = get_time_ms(); } /** * Unredirect all windows. */ static void redir_stop(session_t *ps) { if (ps->redirected) { #ifdef DEBUG_REDIR print_timestamp(ps); printf_dbgf("(): Screen unredirected.\n"); #endif // Destroy all Pictures as they expire once windows are unredirected // If we don't destroy them here, looks like the resources are just // kept inaccessible somehow for (win *w = ps->list; w; w = w->next) free_wpaint(ps, w); XCompositeUnredirectSubwindows(ps->dpy, ps->root, CompositeRedirectManual); // Unmap overlay window if (ps->overlay) XUnmapWindow(ps->dpy, ps->overlay); // Must call XSync() here XSync(ps->dpy, False); ps->redirected = false; } } /** * Unredirection timeout callback. */ static bool tmout_unredir_callback(session_t *ps, timeout_t *tmout) { ps->tmout_unredir_hit = true; tmout->enabled = false; return true; } /** * Main loop. */ static bool mainloop(session_t *ps) { // Don't miss timeouts even when we have a LOT of other events! timeout_run(ps); // Process existing events // Sometimes poll() returns 1 but no events are actually read, // causing XNextEvent() to block, I have no idea what's wrong, so we // check for the number of events here. if (XEventsQueued(ps->dpy, QueuedAfterReading)) { XEvent ev = { }; XNextEvent(ps->dpy, &ev); ev_handle(ps, &ev); ps->ev_received = true; return true; } #ifdef CONFIG_DBUS if (ps->o.dbus) { cdbus_loop(ps); } #endif if (ps->reset) return false; // Calculate timeout struct timeval *ptv = NULL; { // Consider ev_received firstly if (ps->ev_received || ps->o.benchmark) { ptv = malloc(sizeof(struct timeval)); ptv->tv_sec = 0L; ptv->tv_usec = 0L; } // Then consider fading timeout else if (!ps->idling) { ptv = malloc(sizeof(struct timeval)); *ptv = ms_to_tv(fade_timeout(ps)); } // Software optimization is to be applied on timeouts that require // immediate painting only if (ptv && ps->o.sw_opti) swopti_handle_timeout(ps, ptv); // Don't continue looping for 0 timeout if (ptv && timeval_isempty(ptv)) { free(ptv); return false; } // Now consider the waiting time of other timeouts time_ms_t tmout_ms = timeout_get_poll_time(ps); if (tmout_ms < TIME_MS_MAX) { if (!ptv) { ptv = malloc(sizeof(struct timeval)); *ptv = ms_to_tv(tmout_ms); } else if (timeval_ms_cmp(ptv, tmout_ms) > 0) { *ptv = ms_to_tv(tmout_ms); } } // Don't continue looping for 0 timeout if (ptv && timeval_isempty(ptv)) { free(ptv); return false; } } // Polling fds_poll(ps, ptv); free(ptv); ptv = NULL; return true; } static void cxinerama_upd_scrs(session_t *ps) { #ifdef CONFIG_XINERAMA free_xinerama_info(ps); if (!ps->o.xinerama_shadow_crop || !ps->xinerama_exists) return; if (!XineramaIsActive(ps->dpy)) return; ps->xinerama_scrs = XineramaQueryScreens(ps->dpy, &ps->xinerama_nscrs); // Just in case the shit hits the fan... if (!ps->xinerama_nscrs) { cxfree(ps->xinerama_scrs); ps->xinerama_scrs = NULL; return; } ps->xinerama_scr_regs = allocchk(malloc(sizeof(XserverRegion *) * ps->xinerama_nscrs)); for (int i = 0; i < ps->xinerama_nscrs; ++i) { const XineramaScreenInfo * const s = &ps->xinerama_scrs[i]; XRectangle r = { .x = s->x_org, .y = s->y_org, .width = s->width, .height = s->height }; ps->xinerama_scr_regs[i] = XFixesCreateRegion(ps->dpy, &r, 1); } #endif } /** * Initialize a session. * * @param ps_old old session, from which the function will take the X * connection, then free it * @param argc number of commandline arguments * @param argv commandline arguments */ static session_t * session_init(session_t *ps_old, int argc, char **argv) { const static session_t s_def = { .dpy = NULL, .scr = 0, .vis = NULL, .depth = 0, .root = None, .root_height = 0, .root_width = 0, // .root_damage = None, .overlay = None, .root_tile_fill = false, .root_tile_paint = PAINT_INIT, .screen_reg = None, .tgt_picture = None, .tgt_buffer = PAINT_INIT, .root_dbe = None, .reg_win = None, .o = { .config_file = NULL, .display = NULL, .backend = BKEND_XRENDER, .glx_no_stencil = false, .glx_copy_from_front = false, #ifdef CONFIG_VSYNC_OPENGL_GLSL .glx_prog_win = GLX_PROG_MAIN_INIT, #endif .mark_wmwin_focused = false, .mark_ovredir_focused = false, .fork_after_register = false, .synchronize = false, .detect_rounded_corners = false, .paint_on_overlay = false, .resize_damage = 0, .unredir_if_possible = false, .unredir_if_possible_blacklist = NULL, .unredir_if_possible_delay = 0, .redirected_force = UNSET, .stoppaint_force = UNSET, .dbus = false, .benchmark = 0, .benchmark_wid = None, .logpath = NULL, .refresh_rate = 0, .sw_opti = false, .vsync = VSYNC_NONE, .dbe = false, .vsync_aggressive = false, .wintype_shadow = { false }, .shadow_red = 0.0, .shadow_green = 0.0, .shadow_blue = 0.0, .shadow_radius = 12, .shadow_offset_x = -15, .shadow_offset_y = -15, .shadow_opacity = .75, .clear_shadow = false, .shadow_blacklist = NULL, .shadow_ignore_shaped = false, .respect_prop_shadow = false, .xinerama_shadow_crop = false, .wintype_fade = { false }, .fade_in_step = 0.028 * OPAQUE, .fade_out_step = 0.03 * OPAQUE, .fade_delta = 10, .no_fading_openclose = false, .no_fading_destroyed_argb = false, .fade_blacklist = NULL, .wintype_opacity = { 0.0 }, .inactive_opacity = 0, .inactive_opacity_override = false, .active_opacity = 0, .frame_opacity = 0.0, .detect_client_opacity = false, .alpha_step = 0.03, .blur_background = false, .blur_background_frame = false, .blur_background_fixed = false, .blur_background_blacklist = NULL, .blur_kerns = { NULL }, .inactive_dim = 0.0, .inactive_dim_fixed = false, .invert_color_list = NULL, .opacity_rules = NULL, .wintype_focus = { false }, .use_ewmh_active_win = false, .focus_blacklist = NULL, .detect_transient = false, .detect_client_leader = false, .track_focus = false, .track_wdata = false, .track_leader = false, }, .pfds_read = NULL, .pfds_write = NULL, .pfds_except = NULL, .nfds_max = 0, .tmout_lst = NULL, .all_damage = None, .all_damage_last = { None }, .time_start = { 0, 0 }, .redirected = false, .alpha_picts = NULL, .reg_ignore_expire = false, .idling = false, .fade_time = 0L, .ignore_head = NULL, .ignore_tail = NULL, .reset = false, .expose_rects = NULL, .size_expose = 0, .n_expose = 0, .list = NULL, .active_win = NULL, .active_leader = None, .black_picture = None, .cshadow_picture = None, .white_picture = None, .gaussian_map = NULL, .cgsize = 0, .shadow_corner = NULL, .shadow_top = NULL, .refresh_rate = 0, .refresh_intv = 0UL, .paint_tm_offset = 0L, #ifdef CONFIG_VSYNC_DRM .drm_fd = -1, #endif .xfixes_event = 0, .xfixes_error = 0, .damage_event = 0, .damage_error = 0, .render_event = 0, .render_error = 0, .composite_event = 0, .composite_error = 0, .composite_opcode = 0, .has_name_pixmap = false, .shape_exists = false, .shape_event = 0, .shape_error = 0, .randr_exists = 0, .randr_event = 0, .randr_error = 0, #ifdef CONFIG_VSYNC_OPENGL .glx_exists = false, .glx_event = 0, .glx_error = 0, #endif .dbe_exists = false, .xrfilter_convolution_exists = false, .atom_opacity = None, .atom_frame_extents = None, .atom_client = None, .atom_name = None, .atom_name_ewmh = None, .atom_class = None, .atom_role = None, .atom_transient = None, .atom_ewmh_active_win = None, .atom_compton_shadow = None, .atom_win_type = None, .atoms_wintypes = { 0 }, .track_atom_lst = NULL, #ifdef CONFIG_DBUS .dbus_conn = NULL, .dbus_service = NULL, #endif }; // Allocate a session and copy default values into it session_t *ps = malloc(sizeof(session_t)); memcpy(ps, &s_def, sizeof(session_t)); ps_g = ps; ps->ignore_tail = &ps->ignore_head; gettimeofday(&ps->time_start, NULL); wintype_arr_enable(ps->o.wintype_focus); ps->o.wintype_focus[WINTYPE_UNKNOWN] = false; ps->o.wintype_focus[WINTYPE_NORMAL] = false; ps->o.wintype_focus[WINTYPE_UTILITY] = false; // First pass get_cfg(ps, argc, argv, true); // Inherit old Display if possible, primarily for resource leak checking if (ps_old && ps_old->dpy) ps->dpy = ps_old->dpy; // Open Display if (!ps->dpy) { ps->dpy = XOpenDisplay(ps->o.display); if (!ps->dpy) { printf_errfq(1, "(): Can't open display."); } } XSetErrorHandler(xerror); if (ps->o.synchronize) { XSynchronize(ps->dpy, 1); } ps->scr = DefaultScreen(ps->dpy); ps->root = RootWindow(ps->dpy, ps->scr); ps->vis = DefaultVisual(ps->dpy, ps->scr); ps->depth = DefaultDepth(ps->dpy, ps->scr); // Start listening to events on root earlier to catch all possible // root geometry changes XSelectInput(ps->dpy, ps->root, SubstructureNotifyMask | ExposureMask | StructureNotifyMask | PropertyChangeMask); XFlush(ps->dpy); ps->root_width = DisplayWidth(ps->dpy, ps->scr); ps->root_height = DisplayHeight(ps->dpy, ps->scr); if (!XRenderQueryExtension(ps->dpy, &ps->render_event, &ps->render_error)) { fprintf(stderr, "No render extension\n"); exit(1); } if (!XQueryExtension(ps->dpy, COMPOSITE_NAME, &ps->composite_opcode, &ps->composite_event, &ps->composite_error)) { fprintf(stderr, "No composite extension\n"); exit(1); } { int composite_major = 0, composite_minor = 0; XCompositeQueryVersion(ps->dpy, &composite_major, &composite_minor); if (!ps->o.no_name_pixmap && (composite_major > 0 || composite_minor >= 2)) { ps->has_name_pixmap = true; } } if (!XDamageQueryExtension(ps->dpy, &ps->damage_event, &ps->damage_error)) { fprintf(stderr, "No damage extension\n"); exit(1); } if (!XFixesQueryExtension(ps->dpy, &ps->xfixes_event, &ps->xfixes_error)) { fprintf(stderr, "No XFixes extension\n"); exit(1); } // Build a safe representation of display name { char *display_repr = DisplayString(ps->dpy); if (!display_repr) display_repr = "unknown"; display_repr = mstrcpy(display_repr); // Convert all special characters in display_repr name to underscore { char *pdisp = display_repr; while (*pdisp) { if (!isalnum(*pdisp)) *pdisp = '_'; ++pdisp; } } ps->o.display_repr = display_repr; } // Second pass get_cfg(ps, argc, argv, false); // Query X Shape if (XShapeQueryExtension(ps->dpy, &ps->shape_event, &ps->shape_error)) { ps->shape_exists = true; } if (ps->o.xrender_sync_fence) { #ifdef CONFIG_XSYNC // Query X Sync if (XSyncQueryExtension(ps->dpy, &ps->xsync_event, &ps->xsync_error)) { // TODO: Fencing may require version >= 3.0? int major_version_return = 0, minor_version_return = 0; if (XSyncInitialize(ps->dpy, &major_version_return, &minor_version_return)) ps->xsync_exists = true; } if (!ps->xsync_exists) { printf_errf("(): X Sync extension not found. No X Sync fence sync is " "possible."); exit(1); } #else printf_errf("(): X Sync support not compiled in. --xrender-sync-fence " "can't work."); exit(1); #endif } // Query X RandR if ((ps->o.sw_opti && !ps->o.refresh_rate) || ps->o.xinerama_shadow_crop) { if (XRRQueryExtension(ps->dpy, &ps->randr_event, &ps->randr_error)) ps->randr_exists = true; else printf_errf("(): No XRandR extension, automatic screen change " "detection impossible."); } // Query X DBE extension if (ps->o.dbe) { int dbe_ver_major = 0, dbe_ver_minor = 0; if (XdbeQueryExtension(ps->dpy, &dbe_ver_major, &dbe_ver_minor)) if (dbe_ver_major >= 1) ps->dbe_exists = true; else fprintf(stderr, "DBE extension version too low. Double buffering " "impossible.\n"); else { fprintf(stderr, "No DBE extension. Double buffering impossible.\n"); } if (!ps->dbe_exists) ps->o.dbe = false; } // Query X Xinerama extension if (ps->o.xinerama_shadow_crop) { #ifdef CONFIG_XINERAMA int xinerama_event = 0, xinerama_error = 0; if (XineramaQueryExtension(ps->dpy, &xinerama_event, &xinerama_error)) ps->xinerama_exists = true; #else printf_errf("(): Xinerama support not compiled in."); #endif } rebuild_screen_reg(ps); // Overlay must be initialized before double buffer, and before creation // of OpenGL context. if (ps->o.paint_on_overlay) init_overlay(ps); // Initialize DBE if (ps->o.dbe && BKEND_XRENDER != ps->o.backend) { printf_errf("(): DBE couldn't be used on GLX backend."); ps->o.dbe = false; } if (ps->o.dbe && !init_dbe(ps)) exit(1); // Initialize OpenGL as early as possible if (bkend_use_glx(ps)) { #ifdef CONFIG_VSYNC_OPENGL if (!glx_init(ps, true)) exit(1); #else printf_errfq(1, "(): GLX backend support not compiled in."); #endif } // Initialize window GL shader if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) { #ifdef CONFIG_VSYNC_OPENGL_GLSL if (!glx_load_prog_main(ps, NULL, ps->o.glx_fshader_win_str, &ps->o.glx_prog_win)) exit(1); #else printf_errf("(): GLSL supported not compiled in, can't load shader."); exit(1); #endif } // Initialize software optimization if (ps->o.sw_opti) ps->o.sw_opti = swopti_init(ps); // Monitor screen changes if vsync_sw is enabled and we are using // an auto-detected refresh rate, or when Xinerama features are enabled if (ps->randr_exists && ((ps->o.sw_opti && !ps->o.refresh_rate) || ps->o.xinerama_shadow_crop)) XRRSelectInput(ps->dpy, ps->root, RRScreenChangeNotifyMask); // Initialize VSync if (!vsync_init(ps)) exit(1); cxinerama_upd_scrs(ps); // Create registration window if (!ps->reg_win && !register_cm(ps)) exit(1); init_atoms(ps); init_alpha_picts(ps); ps->gaussian_map = make_gaussian_map(ps->o.shadow_radius); presum_gaussian(ps, ps->gaussian_map); { XRenderPictureAttributes pa; pa.subwindow_mode = IncludeInferiors; ps->root_picture = XRenderCreatePicture(ps->dpy, ps->root, XRenderFindVisualFormat(ps->dpy, ps->vis), CPSubwindowMode, &pa); if (ps->o.paint_on_overlay) { ps->tgt_picture = XRenderCreatePicture(ps->dpy, ps->overlay, XRenderFindVisualFormat(ps->dpy, ps->vis), CPSubwindowMode, &pa); } else { ps->tgt_picture = ps->root_picture; } } // Initialize filters, must be preceded by OpenGL context creation if (!init_filters(ps)) exit(1); ps->black_picture = solid_picture(ps, true, 1, 0, 0, 0); ps->white_picture = solid_picture(ps, true, 1, 1, 1, 1); // Generates another Picture for shadows if the color is modified by // user if (!ps->o.shadow_red && !ps->o.shadow_green && !ps->o.shadow_blue) { ps->cshadow_picture = ps->black_picture; } else { ps->cshadow_picture = solid_picture(ps, true, 1, ps->o.shadow_red, ps->o.shadow_green, ps->o.shadow_blue); } fds_insert(ps, ConnectionNumber(ps->dpy), POLLIN); ps->tmout_unredir = timeout_insert(ps, ps->o.unredir_if_possible_delay, tmout_unredir_callback, NULL); ps->tmout_unredir->enabled = false; XGrabServer(ps->dpy); { Window root_return, parent_return; Window *children; unsigned int nchildren; XQueryTree(ps->dpy, ps->root, &root_return, &parent_return, &children, &nchildren); for (unsigned i = 0; i < nchildren; i++) { add_win(ps, children[i], i ? children[i-1] : None); } cxfree(children); } if (ps->o.track_focus) { recheck_focus(ps); } XUngrabServer(ps->dpy); // ALWAYS flush after XUngrabServer()! XFlush(ps->dpy); // Initialize DBus if (ps->o.dbus) { #ifdef CONFIG_DBUS cdbus_init(ps); if (!ps->dbus_conn) { cdbus_destroy(ps); ps->o.dbus = false; } #else printf_errfq(1, "(): DBus support not compiled in!"); #endif } // Fork to background, if asked if (ps->o.fork_after_register) { if (!fork_after(ps)) { session_destroy(ps); return NULL; } } // Redirect output stream if (ps->o.fork_after_register || ps->o.logpath) ostream_reopen(ps, NULL); write_pid(ps); // Free the old session if (ps_old) free(ps_old); return ps; } /** * Destroy a session. * * Does not close the X connection or free the session_t * structure, though. * * @param ps session to destroy */ static void session_destroy(session_t *ps) { redir_stop(ps); // Stop listening to events on root window XSelectInput(ps->dpy, ps->root, 0); #ifdef CONFIG_DBUS // Kill DBus connection if (ps->o.dbus) cdbus_destroy(ps); free(ps->dbus_service); #endif // Free window linked list { win *next = NULL; for (win *w = ps->list; w; w = next) { // Must be put here to avoid segfault next = w->next; if (IsViewable == w->a.map_state && !w->destroyed) win_ev_stop(ps, w); free_win_res(ps, w); free(w); } ps->list = NULL; } // Free alpha_picts { const int max = round(1.0 / ps->o.alpha_step) + 1; for (int i = 0; i < max; ++i) free_picture(ps, &ps->alpha_picts[i]); free(ps->alpha_picts); ps->alpha_picts = NULL; } #ifdef CONFIG_C2 // Free blacklists free_wincondlst(&ps->o.shadow_blacklist); free_wincondlst(&ps->o.fade_blacklist); free_wincondlst(&ps->o.focus_blacklist); free_wincondlst(&ps->o.invert_color_list); free_wincondlst(&ps->o.blur_background_blacklist); free_wincondlst(&ps->o.opacity_rules); free_wincondlst(&ps->o.paint_blacklist); free_wincondlst(&ps->o.unredir_if_possible_blacklist); #endif // Free tracked atom list { latom_t *next = NULL; for (latom_t *this = ps->track_atom_lst; this; this = next) { next = this->next; free(this); } ps->track_atom_lst = NULL; } // Free ignore linked list { ignore_t *next = NULL; for (ignore_t *ign = ps->ignore_head; ign; ign = next) { next = ign->next; free(ign); } // Reset head and tail ps->ignore_head = NULL; ps->ignore_tail = &ps->ignore_head; } // Free cshadow_picture and black_picture if (ps->cshadow_picture == ps->black_picture) ps->cshadow_picture = None; else free_picture(ps, &ps->cshadow_picture); free_picture(ps, &ps->black_picture); free_picture(ps, &ps->white_picture); // Free tgt_{buffer,picture} and root_picture if (ps->tgt_buffer.pict == ps->tgt_picture) ps->tgt_buffer.pict = None; if (ps->tgt_picture == ps->root_picture) ps->tgt_picture = None; else free_picture(ps, &ps->tgt_picture); free_fence(ps, &ps->tgt_buffer_fence); free_picture(ps, &ps->root_picture); free_paint(ps, &ps->tgt_buffer); // Free other X resources free_root_tile(ps); free_region(ps, &ps->screen_reg); free_region(ps, &ps->all_damage); for (int i = 0; i < CGLX_MAX_BUFFER_AGE; ++i) free_region(ps, &ps->all_damage_last[i]); free(ps->expose_rects); free(ps->shadow_corner); free(ps->shadow_top); free(ps->gaussian_map); free(ps->o.config_file); free(ps->o.write_pid_path); free(ps->o.display); free(ps->o.display_repr); free(ps->o.logpath); for (int i = 0; i < MAX_BLUR_PASS; ++i) { free(ps->o.blur_kerns[i]); free(ps->blur_kerns_cache[i]); } free(ps->pfds_read); free(ps->pfds_write); free(ps->pfds_except); free(ps->o.glx_fshader_win_str); free_xinerama_info(ps); #ifdef CONFIG_VSYNC_OPENGL glx_destroy(ps); #endif // Free double buffer if (ps->root_dbe) { XdbeDeallocateBackBufferName(ps->dpy, ps->root_dbe); ps->root_dbe = None; } #ifdef CONFIG_VSYNC_DRM // Close file opened for DRM VSync if (ps->drm_fd >= 0) { close(ps->drm_fd); ps->drm_fd = -1; } #endif // Release overlay window if (ps->overlay) { XCompositeReleaseOverlayWindow(ps->dpy, ps->overlay); ps->overlay = None; } // Free reg_win if (ps->reg_win) { XDestroyWindow(ps->dpy, ps->reg_win); ps->reg_win = None; } // Flush all events XSync(ps->dpy, True); #ifdef DEBUG_XRC // Report about resource leakage xrc_report_xid(); #endif // Free timeouts ps->tmout_unredir = NULL; timeout_clear(ps); if (ps == ps_g) ps_g = NULL; } /* static inline void dump_img(session_t *ps) { int len = 0; unsigned char *d = glx_take_screenshot(ps, &len); write_binary_data("/tmp/dump.raw", d, len); free(d); } */ /** * Do the actual work. * * @param ps current session */ static void session_run(session_t *ps) { win *t; if (ps->o.sw_opti) ps->paint_tm_offset = get_time_timeval().tv_usec; ps->reg_ignore_expire = true; t = paint_preprocess(ps, ps->list); if (ps->redirected) paint_all(ps, None, None, t); // Initialize idling ps->idling = false; // Main loop while (!ps->reset) { ps->ev_received = false; while (mainloop(ps)) continue; if (ps->o.benchmark) { if (ps->o.benchmark_wid) { win *w = find_win(ps, ps->o.benchmark_wid); if (!w) { printf_errf("(): Couldn't find specified benchmark window."); session_destroy(ps); exit(1); } add_damage_win(ps, w); } else { force_repaint(ps); } } // idling will be turned off during paint_preprocess() if needed ps->idling = true; t = paint_preprocess(ps, ps->list); ps->tmout_unredir_hit = false; // If the screen is unredirected, free all_damage to stop painting if (!ps->redirected || ON == ps->o.stoppaint_force) free_region(ps, &ps->all_damage); XserverRegion all_damage_orig = None; if (ps->o.resize_damage > 0) all_damage_orig = copy_region(ps, ps->all_damage); resize_region(ps, ps->all_damage, ps->o.resize_damage); if (ps->all_damage && !is_region_empty(ps, ps->all_damage, NULL)) { static int paint = 0; paint_all(ps, ps->all_damage, all_damage_orig, t); ps->reg_ignore_expire = false; paint++; if (ps->o.benchmark && paint >= ps->o.benchmark) exit(0); XSync(ps->dpy, False); ps->all_damage = None; } free_region(ps, &all_damage_orig); if (ps->idling) ps->fade_time = 0L; } } /** * Turn on the program reset flag. * * This will result in compton resetting itself after next paint. */ static void reset_enable(int __attribute__((unused)) signum) { session_t * const ps = ps_g; ps->reset = true; } /** * The function that everybody knows. */ int main(int argc, char **argv) { // Set locale so window names with special characters are interpreted // correctly setlocale(LC_ALL, ""); // Set up SIGUSR1 signal handler to reset program { sigset_t block_mask; sigemptyset(&block_mask); const struct sigaction action= { .sa_handler = reset_enable, .sa_mask = block_mask, .sa_flags = 0 }; sigaction(SIGUSR1, &action, NULL); } // Main loop session_t *ps_old = ps_g; while (1) { ps_g = session_init(ps_old, argc, argv); if (!ps_g) { printf_errf("(): Failed to create new session."); return 1; } session_run(ps_g); ps_old = ps_g; session_destroy(ps_g); } free(ps_g); return 0; } compton-0.1~beta2+20150922/src/compton.h000066400000000000000000000750211260012102400174550ustar00rootroot00000000000000/** * compton.h */ // Throw everything in here. // === Includes === #include "common.h" #include #include #include #include #include #include #include #ifdef CONFIG_VSYNC_DRM #include // We references some definitions in drm.h, which could also be found in // /usr/src/linux/include/drm/drm.h, but that path is probably even less // reliable than libdrm #include #include #include #endif // == Functions == // inline functions must be made static to compile correctly under clang: // http://clang.llvm.org/compatibility.html#inline // Helper functions static void discard_ignore(session_t *ps, unsigned long sequence); static void set_ignore(session_t *ps, unsigned long sequence); /** * Ignore X errors caused by next X request. */ static inline void set_ignore_next(session_t *ps) { set_ignore(ps, NextRequest(ps->dpy)); } static int should_ignore(session_t *ps, unsigned long sequence); /** * Reset filter on a Picture. */ static inline void xrfilter_reset(session_t *ps, Picture p) { XRenderSetPictureFilter(ps->dpy, p, "Nearest", NULL, 0); } /** * Subtract two unsigned long values. * * Truncate to 0 if the result is negative. */ static inline unsigned long __attribute__((const)) sub_unslong(unsigned long a, unsigned long b) { return (a > b) ? a - b : 0; } /** * Set a bool array of all wintypes to true. */ static inline void wintype_arr_enable(bool arr[]) { wintype_t i; for (i = 0; i < NUM_WINTYPES; ++i) { arr[i] = true; } } /** * Set a switch_t array of all unset wintypes to true. */ static inline void wintype_arr_enable_unset(switch_t arr[]) { wintype_t i; for (i = 0; i < NUM_WINTYPES; ++i) if (UNSET == arr[i]) arr[i] = ON; } /** * Check if a window ID exists in an array of window IDs. * * @param arr the array of window IDs * @param count amount of elements in the array * @param wid window ID to search for */ static inline bool array_wid_exists(const Window *arr, int count, Window wid) { while (count--) { if (arr[count] == wid) { return true; } } return false; } /** * Convert a geometry_t value to XRectangle. */ static inline XRectangle geom_to_rect(session_t *ps, const geometry_t *src, const XRectangle *def) { XRectangle rect_def = { .x = 0, .y = 0, .width = ps->root_width, .height = ps->root_height }; if (!def) def = &rect_def; XRectangle rect = { .x = src->x, .y = src->y, .width = src->wid, .height = src->hei }; if (src->wid < 0) rect.width = def->width; if (src->hei < 0) rect.height = def->height; if (-1 == src->x) rect.x = def->x; else if (src->x < 0) rect.x = ps->root_width + rect.x + 2 - rect.width; if (-1 == src->y) rect.y = def->y; else if (src->y < 0) rect.y = ps->root_height + rect.y + 2 - rect.height; return rect; } /** * Convert a XRectangle to a XServerRegion. */ static inline XserverRegion rect_to_reg(session_t *ps, const XRectangle *src) { if (!src) return None; XRectangle bound = { .x = 0, .y = 0, .width = ps->root_width, .height = ps->root_height }; XRectangle res = { }; rect_crop(&res, src, &bound); if (res.width && res.height) return XFixesCreateRegion(ps->dpy, &res, 1); return None; } /** * Destroy a Picture. */ inline static void free_picture(session_t *ps, Picture *p) { if (*p) { XRenderFreePicture(ps->dpy, *p); *p = None; } } /** * Destroy a Pixmap. */ inline static void free_pixmap(session_t *ps, Pixmap *p) { if (*p) { XFreePixmap(ps->dpy, *p); *p = None; } } /** * Destroy a Damage. */ inline static void free_damage(session_t *ps, Damage *p) { if (*p) { // BadDamage will be thrown if the window is destroyed set_ignore_next(ps); XDamageDestroy(ps->dpy, *p); *p = None; } } /** * Destroy a condition list. */ static inline void free_wincondlst(c2_lptr_t **pcondlst) { #ifdef CONFIG_C2 while ((*pcondlst = c2_free_lptr(*pcondlst))) continue; #endif } /** * Free Xinerama screen info. */ static inline void free_xinerama_info(session_t *ps) { #ifdef CONFIG_XINERAMA if (ps->xinerama_scr_regs) { for (int i = 0; i < ps->xinerama_nscrs; ++i) free_region(ps, &ps->xinerama_scr_regs[i]); free(ps->xinerama_scr_regs); } cxfree(ps->xinerama_scrs); ps->xinerama_scrs = NULL; ps->xinerama_nscrs = 0; #endif } /** * Check whether a paint_t contains enough data. */ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) { // Don't check for presence of Pixmap here, because older X Composite doesn't // provide it if (!ppaint) return false; if (bkend_use_xrender(ps) && !ppaint->pict) return false; #ifdef CONFIG_VSYNC_OPENGL if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex, None)) return false; #endif return true; } /** * Bind texture in paint_t if we are using GLX backend. */ static inline bool paint_bind_tex_real(session_t *ps, paint_t *ppaint, unsigned wid, unsigned hei, unsigned depth, bool force) { #ifdef CONFIG_VSYNC_OPENGL if (!ppaint->pixmap) return false; if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap)) return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth); #endif return true; } static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, unsigned wid, unsigned hei, unsigned depth, bool force) { if (BKEND_GLX == ps->o.backend) return paint_bind_tex_real(ps, ppaint, wid, hei, depth, force); return true; } /** * Free data in a reg_data_t. */ static inline void free_reg_data(reg_data_t *pregd) { cxfree(pregd->rects); pregd->rects = NULL; pregd->nrects = 0; } /** * Free paint_t. */ static inline void free_paint(session_t *ps, paint_t *ppaint) { free_paint_glx(ps, ppaint); free_picture(ps, &ppaint->pict); free_pixmap(ps, &ppaint->pixmap); } /** * Free w->paint. */ static inline void free_wpaint(session_t *ps, win *w) { free_paint(ps, &w->paint); free_fence(ps, &w->fence); } /** * Destroy all resources in a struct _win. */ static inline void free_win_res(session_t *ps, win *w) { free_win_res_glx(ps, w); free_region(ps, &w->extents); free_paint(ps, &w->paint); free_region(ps, &w->border_size); free_paint(ps, &w->shadow_paint); free_damage(ps, &w->damage); free_region(ps, &w->reg_ignore); free(w->name); free(w->class_instance); free(w->class_general); free(w->role); } /** * Free root tile related things. */ static inline void free_root_tile(session_t *ps) { free_picture(ps, &ps->root_tile_paint.pict); free_texture(ps, &ps->root_tile_paint.ptex); if (ps->root_tile_fill) free_pixmap(ps, &ps->root_tile_paint.pixmap); ps->root_tile_paint.pixmap = None; ps->root_tile_fill = false; } /** * Get current system clock in milliseconds. */ static inline time_ms_t get_time_ms(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec % SEC_WRAP * 1000 + tv.tv_usec / 1000; } /** * Convert time from milliseconds to struct timeval. */ static inline struct timeval ms_to_tv(int timeout) { return (struct timeval) { .tv_sec = timeout / MS_PER_SEC, .tv_usec = timeout % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC) }; } /** * Whether an event is DamageNotify. */ static inline bool isdamagenotify(session_t *ps, const XEvent *ev) { return ps->damage_event + XDamageNotify == ev->type; } /** * Create a XTextProperty of a single string. */ static inline XTextProperty * make_text_prop(session_t *ps, char *str) { XTextProperty *pprop = ccalloc(1, XTextProperty); if (XmbTextListToTextProperty(ps->dpy, &str, 1, XStringStyle, pprop)) { cxfree(pprop->value); free(pprop); pprop = NULL; } return pprop; } /** * Set a single-string text property on a window. */ static inline bool wid_set_text_prop(session_t *ps, Window wid, Atom prop_atom, char *str) { XTextProperty *pprop = make_text_prop(ps, str); if (!pprop) { printf_errf("(\"%s\"): Failed to make text property.", str); return false; } XSetTextProperty(ps->dpy, wid, pprop, prop_atom); cxfree(pprop->value); cxfree(pprop); return true; } static void run_fade(session_t *ps, win *w, unsigned steps); static void set_fade_callback(session_t *ps, win *w, void (*callback) (session_t *ps, win *w), bool exec_callback); /** * Execute fade callback of a window if fading finished. */ static inline void check_fade_fin(session_t *ps, win *w) { if (w->fade_callback && w->opacity == w->opacity_tgt) { // Must be the last line as the callback could destroy w! set_fade_callback(ps, w, NULL, true); } } static void set_fade_callback(session_t *ps, win *w, void (*callback) (session_t *ps, win *w), bool exec_callback); static double gaussian(double r, double x, double y); static conv * make_gaussian_map(double r); static unsigned char sum_gaussian(conv *map, double opacity, int x, int y, int width, int height); static void presum_gaussian(session_t *ps, conv *map); static XImage * make_shadow(session_t *ps, double opacity, int width, int height); static bool win_build_shadow(session_t *ps, win *w, double opacity); static Picture solid_picture(session_t *ps, bool argb, double a, double r, double g, double b); /** * Stop listening for events on a particular window. */ static inline void win_ev_stop(session_t *ps, win *w) { // Will get BadWindow if the window is destroyed set_ignore_next(ps); XSelectInput(ps->dpy, w->id, 0); if (w->client_win) { set_ignore_next(ps); XSelectInput(ps->dpy, w->client_win, 0); } if (ps->shape_exists) { set_ignore_next(ps); XShapeSelectInput(ps->dpy, w->id, 0); } } /** * Get the children of a window. * * @param ps current session * @param w window to check * @param children [out] an array of child window IDs * @param nchildren [out] number of children * @return 1 if successful, 0 otherwise */ static inline bool wid_get_children(session_t *ps, Window w, Window **children, unsigned *nchildren) { Window troot, tparent; if (!XQueryTree(ps->dpy, w, &troot, &tparent, children, nchildren)) { *nchildren = 0; return false; } return true; } /** * Check if a window is bounding-shaped. */ static inline bool wid_bounding_shaped(const session_t *ps, Window wid) { if (ps->shape_exists) { Bool bounding_shaped = False, clip_shaped = False; int x_bounding, y_bounding, x_clip, y_clip; unsigned int w_bounding, h_bounding, w_clip, h_clip; XShapeQueryExtents(ps->dpy, wid, &bounding_shaped, &x_bounding, &y_bounding, &w_bounding, &h_bounding, &clip_shaped, &x_clip, &y_clip, &w_clip, &h_clip); return bounding_shaped; } return false; } /** * Determine if a window change affects reg_ignore and set * reg_ignore_expire accordingly. */ static inline void update_reg_ignore_expire(session_t *ps, const win *w) { if (w->to_paint && WMODE_SOLID == w->mode) ps->reg_ignore_expire = true; } /** * Check whether a window has WM frames. */ static inline bool __attribute__((pure)) win_has_frame(const win *w) { return w->a.border_width || w->frame_extents.top || w->frame_extents.left || w->frame_extents.right || w->frame_extents.bottom; } /** * Calculate the extents of the frame of the given window based on EWMH * _NET_FRAME_EXTENTS and the X window border width. */ static inline margin_t __attribute__((pure)) win_calc_frame_extents(session_t *ps, const win *w) { margin_t result = w->frame_extents; result.top = max_i(result.top, w->a.border_width); result.left = max_i(result.left, w->a.border_width); result.bottom = max_i(result.bottom, w->a.border_width); result.right = max_i(result.right, w->a.border_width); return result; } static inline void wid_set_opacity_prop(session_t *ps, Window wid, opacity_t val) { const unsigned long v = val; XChangeProperty(ps->dpy, wid, ps->atom_opacity, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &v, 1); } static inline void wid_rm_opacity_prop(session_t *ps, Window wid) { XDeleteProperty(ps->dpy, wid, ps->atom_opacity); } /** * Dump an drawable's info. */ static inline void dump_drawable(session_t *ps, Drawable drawable) { Window rroot = None; int x = 0, y = 0; unsigned width = 0, height = 0, border = 0, depth = 0; if (XGetGeometry(ps->dpy, drawable, &rroot, &x, &y, &width, &height, &border, &depth)) { printf_dbgf("(%#010lx): x = %u, y = %u, wid = %u, hei = %d, b = %u, d = %u\n", drawable, x, y, width, height, border, depth); } else { printf_dbgf("(%#010lx): Failed\n", drawable); } } static void win_rounded_corners(session_t *ps, win *w); /** * Validate a pixmap. * * Detect whether the pixmap is valid with XGetGeometry. Well, maybe there * are better ways. */ static inline bool validate_pixmap(session_t *ps, Pixmap pxmap) { if (!pxmap) return false; Window rroot = None; int rx = 0, ry = 0; unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0; return XGetGeometry(ps->dpy, pxmap, &rroot, &rx, &ry, &rwid, &rhei, &rborder, &rdepth) && rwid && rhei; } /** * Validate pixmap of a window, and destroy pixmap and picture if invalid. */ static inline void win_validate_pixmap(session_t *ps, win *w) { // Destroy pixmap and picture, if invalid if (!validate_pixmap(ps, w->paint.pixmap)) free_paint(ps, &w->paint); } /** * Wrapper of c2_match(). */ static inline bool win_match(session_t *ps, win *w, c2_lptr_t *condlst, const c2_lptr_t **cache) { #ifdef CONFIG_C2 return c2_match(ps, w, condlst, cache); #else return false; #endif } static bool condlst_add(session_t *ps, c2_lptr_t **pcondlst, const char *pattern); static long determine_evmask(session_t *ps, Window wid, win_evmode_t mode); /** * Clear leader cache of all windows. */ static void clear_cache_win_leaders(session_t *ps) { for (win *w = ps->list; w; w = w->next) w->cache_leader = None; } static win * find_toplevel2(session_t *ps, Window wid); /** * Find matched window. */ static inline win * find_win_all(session_t *ps, const Window wid) { if (!wid || PointerRoot == wid || wid == ps->root || wid == ps->overlay) return NULL; win *w = find_win(ps, wid); if (!w) w = find_toplevel(ps, wid); if (!w) w = find_toplevel2(ps, wid); return w; } static Window win_get_leader_raw(session_t *ps, win *w, int recursions); /** * Get the leader of a window. * * This function updates w->cache_leader if necessary. */ static inline Window win_get_leader(session_t *ps, win *w) { return win_get_leader_raw(ps, w, 0); } /** * Return whether a window group is really focused. * * @param leader leader window ID * @return true if the window group is focused, false otherwise */ static inline bool group_is_focused(session_t *ps, Window leader) { if (!leader) return false; for (win *w = ps->list; w; w = w->next) { if (win_get_leader(ps, w) == leader && !w->destroyed && win_is_focused_real(ps, w)) return true; } return false; } static win * recheck_focus(session_t *ps); static bool get_root_tile(session_t *ps); static void paint_root(session_t *ps, XserverRegion reg_paint); static XserverRegion win_get_region(session_t *ps, win *w, bool use_offset); static XserverRegion win_get_region_noframe(session_t *ps, win *w, bool use_offset); static XserverRegion win_extents(session_t *ps, win *w); static XserverRegion border_size(session_t *ps, win *w, bool use_offset); static Window find_client_win(session_t *ps, Window w); static void get_frame_extents(session_t *ps, win *w, Window client); static win * paint_preprocess(session_t *ps, win *list); static void render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity, bool argb, bool neg, Picture pict, glx_texture_t *ptex, XserverRegion reg_paint, const reg_data_t *pcache_reg #ifdef CONFIG_VSYNC_OPENGL_GLSL , const glx_prog_main_t *pprogram #endif ); #ifdef CONFIG_VSYNC_OPENGL_GLSL #define \ render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) #else #define \ render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg) #endif static inline void win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, XserverRegion reg_paint, const reg_data_t *pcache_reg, Picture pict) { const int dx = (w ? w->a.x: 0) + x; const int dy = (w ? w->a.y: 0) + y; const bool argb = (w && (WMODE_ARGB == w->mode || ps->o.force_win_blend)); const bool neg = (w && w->invert_color); render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), reg_paint, pcache_reg, (w ? &ps->o.glx_prog_win: NULL)); } static inline void set_tgt_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { switch (ps->o.backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer.pict, 0, 0, reg); break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: glx_set_clip(ps, reg, pcache_reg); break; #endif } } static bool xr_blur_dst(session_t *ps, Picture tgt_buffer, int x, int y, int wid, int hei, XFixed **blur_kerns, XserverRegion reg_clip); /** * Normalize a convolution kernel. */ static inline void normalize_conv_kern(int wid, int hei, XFixed *kern) { double sum = 0.0; for (int i = 0; i < wid * hei; ++i) sum += XFixedToDouble(kern[i]); double factor = 1.0 / sum; for (int i = 0; i < wid * hei; ++i) kern[i] = XDoubleToFixed(XFixedToDouble(kern[i]) * factor); } static void paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t); static void add_damage(session_t *ps, XserverRegion damage); static void repair_win(session_t *ps, win *w); static wintype_t wid_get_prop_wintype(session_t *ps, Window w); static void map_win(session_t *ps, Window id); static void finish_map_win(session_t *ps, win *w); static void finish_unmap_win(session_t *ps, win *w); static void unmap_callback(session_t *ps, win *w); static void unmap_win(session_t *ps, win *w); static opacity_t wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def); /** * Reread opacity property of a window. */ static inline void win_update_opacity_prop(session_t *ps, win *w) { w->opacity_prop = wid_get_opacity_prop(ps, w->id, OPAQUE); if (!ps->o.detect_client_opacity || !w->client_win || w->id == w->client_win) w->opacity_prop_client = OPAQUE; else w->opacity_prop_client = wid_get_opacity_prop(ps, w->client_win, OPAQUE); } static double get_opacity_percent(win *w); static void win_determine_mode(session_t *ps, win *w); static void calc_opacity(session_t *ps, win *w); static void calc_dim(session_t *ps, win *w); static Window wid_get_prop_window(session_t *ps, Window wid, Atom aprop); static void win_update_leader(session_t *ps, win *w); static void win_set_leader(session_t *ps, win *w, Window leader); static void win_update_focused(session_t *ps, win *w); /** * Run win_update_focused() on all windows with the same leader window. * * @param leader leader window ID */ static inline void group_update_focused(session_t *ps, Window leader) { if (!leader) return; for (win *w = ps->list; w; w = w->next) { if (win_get_leader(ps, w) == leader && !w->destroyed) win_update_focused(ps, w); } return; } static inline void win_set_focused(session_t *ps, win *w, bool focused); static void win_on_focus_change(session_t *ps, win *w); static void win_determine_fade(session_t *ps, win *w); static void win_update_shape_raw(session_t *ps, win *w); static void win_update_shape(session_t *ps, win *w); static void win_update_prop_shadow_raw(session_t *ps, win *w); static void win_update_prop_shadow(session_t *ps, win *w); static void win_set_shadow(session_t *ps, win *w, bool shadow_new); static void win_determine_shadow(session_t *ps, win *w); static void win_set_invert_color(session_t *ps, win *w, bool invert_color_new); static void win_determine_invert_color(session_t *ps, win *w); static void win_set_blur_background(session_t *ps, win *w, bool blur_background_new); static void win_determine_blur_background(session_t *ps, win *w); static void win_on_wtype_change(session_t *ps, win *w); static void win_on_factor_change(session_t *ps, win *w); static void win_upd_run(session_t *ps, win *w, win_upd_t *pupd); static void calc_win_size(session_t *ps, win *w); static void calc_shadow_geometry(session_t *ps, win *w); static void win_upd_wintype(session_t *ps, win *w); static void win_mark_client(session_t *ps, win *w, Window client); static void win_unmark_client(session_t *ps, win *w); static void win_recheck_client(session_t *ps, win *w); static bool add_win(session_t *ps, Window id, Window prev); static void restack_win(session_t *ps, win *w, Window new_above); static void configure_win(session_t *ps, XConfigureEvent *ce); static void circulate_win(session_t *ps, XCirculateEvent *ce); static void finish_destroy_win(session_t *ps, Window id); static void destroy_callback(session_t *ps, win *w); static void destroy_win(session_t *ps, Window id); static void damage_win(session_t *ps, XDamageNotifyEvent *de); static int xerror(Display *dpy, XErrorEvent *ev); static void expose_root(session_t *ps, XRectangle *rects, int nrects); static Window wid_get_prop_window(session_t *ps, Window wid, Atom aprop); static bool wid_get_name(session_t *ps, Window w, char **name); static bool wid_get_role(session_t *ps, Window w, char **role); static int win_get_prop_str(session_t *ps, win *w, char **tgt, bool (*func_wid_get_prop_str)(session_t *ps, Window wid, char **tgt)); static inline int win_get_name(session_t *ps, win *w) { int ret = win_get_prop_str(ps, w, &w->name, wid_get_name); #ifdef DEBUG_WINDATA printf_dbgf("(%#010lx): client = %#010lx, name = \"%s\", " "ret = %d\n", w->id, w->client_win, w->name, ret); #endif return ret; } static inline int win_get_role(session_t *ps, win *w) { int ret = win_get_prop_str(ps, w, &w->role, wid_get_role); #ifdef DEBUG_WINDATA printf_dbgf("(%#010lx): client = %#010lx, role = \"%s\", " "ret = %d\n", w->id, w->client_win, w->role, ret); #endif return ret; } static bool win_get_class(session_t *ps, win *w); #ifdef DEBUG_EVENTS static int ev_serial(XEvent *ev); static const char * ev_name(session_t *ps, XEvent *ev); static Window ev_window(session_t *ps, XEvent *ev); #endif static void __attribute__ ((noreturn)) usage(int ret); static bool register_cm(session_t *ps); inline static void ev_focus_in(session_t *ps, XFocusChangeEvent *ev); inline static void ev_focus_out(session_t *ps, XFocusChangeEvent *ev); inline static void ev_create_notify(session_t *ps, XCreateWindowEvent *ev); inline static void ev_configure_notify(session_t *ps, XConfigureEvent *ev); inline static void ev_destroy_notify(session_t *ps, XDestroyWindowEvent *ev); inline static void ev_map_notify(session_t *ps, XMapEvent *ev); inline static void ev_unmap_notify(session_t *ps, XUnmapEvent *ev); inline static void ev_reparent_notify(session_t *ps, XReparentEvent *ev); inline static void ev_circulate_notify(session_t *ps, XCirculateEvent *ev); inline static void ev_expose(session_t *ps, XExposeEvent *ev); static void update_ewmh_active_win(session_t *ps); inline static void ev_property_notify(session_t *ps, XPropertyEvent *ev); inline static void ev_damage_notify(session_t *ps, XDamageNotifyEvent *ev); inline static void ev_shape_notify(session_t *ps, XShapeEvent *ev); /** * Get a region of the screen size. */ inline static XserverRegion get_screen_region(session_t *ps) { XRectangle r; r.x = 0; r.y = 0; r.width = ps->root_width; r.height = ps->root_height; return XFixesCreateRegion(ps->dpy, &r, 1); } /** * Resize a region. */ static inline void resize_region(session_t *ps, XserverRegion region, short mod) { if (!mod || !region) return; int nrects = 0, nnewrects = 0; XRectangle *newrects = NULL; XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); if (!rects || !nrects) goto resize_region_end; // Allocate memory for new rectangle list, because I don't know if it's // safe to write in the memory Xlib allocates newrects = calloc(nrects, sizeof(XRectangle)); if (!newrects) { printf_errf("(): Failed to allocate memory."); exit(1); } // Loop through all rectangles for (int i = 0; i < nrects; ++i) { int x1 = max_i(rects[i].x - mod, 0); int y1 = max_i(rects[i].y - mod, 0); int x2 = min_i(rects[i].x + rects[i].width + mod, ps->root_width); int y2 = min_i(rects[i].y + rects[i].height + mod, ps->root_height); int wid = x2 - x1; int hei = y2 - y1; if (wid <= 0 || hei <= 0) continue; newrects[nnewrects].x = x1; newrects[nnewrects].y = y1; newrects[nnewrects].width = wid; newrects[nnewrects].height = hei; ++nnewrects; } // Set region XFixesSetRegion(ps->dpy, region, newrects, nnewrects); resize_region_end: cxfree(rects); free(newrects); } /** * Dump a region. */ static inline void dump_region(const session_t *ps, XserverRegion region) { int nrects = 0; XRectangle *rects = NULL; if (!rects && region) rects = XFixesFetchRegion(ps->dpy, region, &nrects); printf_dbgf("(%#010lx): %d rects\n", region, nrects); if (!rects) return; for (int i = 0; i < nrects; ++i) printf("Rect #%d: %8d, %8d, %8d, %8d\n", i, rects[i].x, rects[i].y, rects[i].width, rects[i].height); putchar('\n'); fflush(stdout); cxfree(rects); } /** * Check if a region is empty. * * Keith Packard said this is slow: * http://lists.freedesktop.org/archives/xorg/2007-November/030467.html * * @param ps current session * @param region region to check for * @param pcache_rects a place to cache the dumped rectangles * @param ncache_nrects a place to cache the number of dumped rectangles */ static inline bool is_region_empty(const session_t *ps, XserverRegion region, reg_data_t *pcache_reg) { int nrects = 0; XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); if (pcache_reg) { pcache_reg->rects = rects; pcache_reg->nrects = nrects; } else cxfree(rects); return !nrects; } /** * Add a window to damaged area. * * @param ps current session * @param w struct _win element representing the window */ static inline void add_damage_win(session_t *ps, win *w) { if (w->extents) { add_damage(ps, copy_region(ps, w->extents)); } } #if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) static bool ev_window_name(session_t *ps, Window wid, char **name); #endif inline static void ev_handle(session_t *ps, XEvent *ev); static bool fork_after(session_t *ps); #ifdef CONFIG_LIBCONFIG /** * Wrapper of libconfig's config_lookup_int. * * To convert int value config_lookup_bool * returns to bool. */ static inline void lcfg_lookup_bool(const config_t *config, const char *path, bool *value) { int ival; if (config_lookup_bool(config, path, &ival)) *value = ival; } /** * Wrapper of libconfig's config_lookup_int. * * To deal with the different value types config_lookup_int * returns in libconfig-1.3 and libconfig-1.4. */ static inline int lcfg_lookup_int(const config_t *config, const char *path, int *value) { #ifndef CONFIG_LIBCONFIG_LEGACY return config_lookup_int(config, path, value); #else long lval; int ret; if ((ret = config_lookup_int(config, path, &lval))) *value = lval; return ret; #endif } static FILE * open_config_file(char *cpath, char **path); static void parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst, const char *name); static void parse_config(session_t *ps, struct options_tmp *pcfgtmp); #endif static void get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass); static void init_atoms(session_t *ps); static void update_refresh_rate(session_t *ps); static bool swopti_init(session_t *ps); static void swopti_handle_timeout(session_t *ps, struct timeval *ptv); #ifdef CONFIG_VSYNC_OPENGL /** * Ensure we have a GLX context. */ static inline bool ensure_glx_context(session_t *ps) { // Create GLX context if (!glx_has_context(ps)) glx_init(ps, false); return ps->psglx->context; } #endif static bool vsync_drm_init(session_t *ps); #ifdef CONFIG_VSYNC_DRM static int vsync_drm_wait(session_t *ps); #endif static bool vsync_opengl_init(session_t *ps); static bool vsync_opengl_oml_init(session_t *ps); static bool vsync_opengl_swc_init(session_t *ps); static bool vsync_opengl_mswc_init(session_t *ps); #ifdef CONFIG_VSYNC_OPENGL static int vsync_opengl_wait(session_t *ps); static int vsync_opengl_oml_wait(session_t *ps); static void vsync_opengl_swc_deinit(session_t *ps); static void vsync_opengl_mswc_deinit(session_t *ps); #endif static void vsync_wait(session_t *ps); static void init_alpha_picts(session_t *ps); static bool init_dbe(session_t *ps); static bool init_overlay(session_t *ps); static void redir_start(session_t *ps); static void redir_stop(session_t *ps); static inline time_ms_t timeout_get_newrun(const timeout_t *ptmout) { 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; } static time_ms_t timeout_get_poll_time(session_t *ps); static void timeout_clear(session_t *ps); static bool tmout_unredir_callback(session_t *ps, timeout_t *tmout); static bool mainloop(session_t *ps); #ifdef CONFIG_XINERAMA static void cxinerama_upd_scrs(session_t *ps); #endif /** * Get the Xinerama screen a window is on. * * Return an index >= 0, or -1 if not found. */ static inline void cxinerama_win_upd_scr(session_t *ps, win *w) { #ifdef CONFIG_XINERAMA w->xinerama_scr = -1; for (XineramaScreenInfo *s = ps->xinerama_scrs; s < ps->xinerama_scrs + ps->xinerama_nscrs; ++s) if (s->x_org <= w->a.x && s->y_org <= w->a.y && s->x_org + s->width >= w->a.x + w->widthb && s->y_org + s->height >= w->a.y + w->heightb) { w->xinerama_scr = s - ps->xinerama_scrs; return; } #endif } static void cxinerama_upd_scrs(session_t *ps); static session_t * session_init(session_t *ps_old, int argc, char **argv); static void session_destroy(session_t *ps); static void session_run(session_t *ps); static void reset_enable(int __attribute__((unused)) signum); compton-0.1~beta2+20150922/src/dbus.c000066400000000000000000001003131260012102400167170ustar00rootroot00000000000000/* * Compton - a compositor for X11 * * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * * Copyright (c) 2011-2013, Christopher Jeffrey * See LICENSE for more information. * */ #include "dbus.h" /** * Initialize D-Bus connection. */ bool cdbus_init(session_t *ps) { DBusError err = { }; // Initialize dbus_error_init(&err); // Connect to D-Bus // Use dbus_bus_get_private() so we can fully recycle it ourselves ps->dbus_conn = dbus_bus_get_private(DBUS_BUS_SESSION, &err); if (dbus_error_is_set(&err)) { printf_errf("(): D-Bus connection failed (%s).", err.message); dbus_error_free(&err); return false; } if (!ps->dbus_conn) { printf_errf("(): D-Bus connection failed for unknown reason."); return false; } // Avoid exiting on disconnect dbus_connection_set_exit_on_disconnect(ps->dbus_conn, false); // Request service name { // Build service name char *service = mstrjoin3(CDBUS_SERVICE_NAME, ".", ps->o.display_repr); ps->dbus_service = service; // Request for the name int ret = dbus_bus_request_name(ps->dbus_conn, service, DBUS_NAME_FLAG_DO_NOT_QUEUE, &err); if (dbus_error_is_set(&err)) { printf_errf("(): Failed to obtain D-Bus name (%s).", err.message); dbus_error_free(&err); } if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret && DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER != ret) { printf_errf("(): Failed to become the primary owner of requested " "D-Bus name (%d).", ret); } } // Add watch handlers if (!dbus_connection_set_watch_functions(ps->dbus_conn, cdbus_callback_add_watch, cdbus_callback_remove_watch, cdbus_callback_watch_toggled, ps, NULL)) { printf_errf("(): Failed to add D-Bus watch functions."); return false; } // Add timeout handlers if (!dbus_connection_set_timeout_functions(ps->dbus_conn, cdbus_callback_add_timeout, cdbus_callback_remove_timeout, cdbus_callback_timeout_toggled, ps, NULL)) { printf_errf("(): Failed to add D-Bus timeout functions."); return false; } // Add match dbus_bus_add_match(ps->dbus_conn, "type='method_call',interface='" CDBUS_INTERFACE_NAME "'", &err); if (dbus_error_is_set(&err)) { printf_errf("(): Failed to add D-Bus match."); dbus_error_free(&err); return false; } return true; } /** * Destroy D-Bus connection. */ void cdbus_destroy(session_t *ps) { if (ps->dbus_conn) { // Release DBus name firstly if (ps->dbus_service) { DBusError err = { }; dbus_error_init(&err); dbus_bus_release_name(ps->dbus_conn, ps->dbus_service, &err); if (dbus_error_is_set(&err)) { printf_errf("(): Failed to release DBus name (%s).", err.message); dbus_error_free(&err); } } // Close and unref the connection dbus_connection_close(ps->dbus_conn); dbus_connection_unref(ps->dbus_conn); } } /** @name DBusTimeout handling */ ///@{ /** * Callback for adding D-Bus timeout. */ static dbus_bool_t cdbus_callback_add_timeout(DBusTimeout *timeout, void *data) { session_t *ps = data; timeout_t *ptmout = timeout_insert(ps, dbus_timeout_get_interval(timeout), cdbus_callback_handle_timeout, timeout); if (ptmout) dbus_timeout_set_data(timeout, ptmout, NULL); return (bool) ptmout; } /** * Callback for removing D-Bus timeout. */ static void cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data) { session_t *ps = data; timeout_t *ptmout = dbus_timeout_get_data(timeout); assert(ptmout); if (ptmout) timeout_drop(ps, ptmout); } /** * Callback for toggling a D-Bus timeout. */ static void cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data) { timeout_t *ptmout = dbus_timeout_get_data(timeout); assert(ptmout); if (ptmout) { ptmout->enabled = dbus_timeout_get_enabled(timeout); // Refresh interval as libdbus doc says: "Whenever a timeout is toggled, // its interval may change." ptmout->interval = dbus_timeout_get_interval(timeout); } } /** * Callback for handling a D-Bus timeout. */ static bool cdbus_callback_handle_timeout(session_t *ps, timeout_t *ptmout) { assert(ptmout && ptmout->data); if (ptmout && ptmout->data) return dbus_timeout_handle(ptmout->data); return false; } ///@} /** @name DBusWatch handling */ ///@{ /** * Callback for adding D-Bus watch. */ static dbus_bool_t cdbus_callback_add_watch(DBusWatch *watch, void *data) { // Leave disabled watches alone if (!dbus_watch_get_enabled(watch)) return TRUE; session_t *ps = data; // Insert the file descriptor fds_insert(ps, dbus_watch_get_unix_fd(watch), cdbus_get_watch_cond(watch)); // Always return true return TRUE; } /** * Callback for removing D-Bus watch. */ static void cdbus_callback_remove_watch(DBusWatch *watch, void *data) { session_t *ps = data; fds_drop(ps, dbus_watch_get_unix_fd(watch), cdbus_get_watch_cond(watch)); } /** * Callback for toggling D-Bus watch status. */ static void cdbus_callback_watch_toggled(DBusWatch *watch, void *data) { if (dbus_watch_get_enabled(watch)) { cdbus_callback_add_watch(watch, data); } else { cdbus_callback_remove_watch(watch, data); } } ///@} /** @name Message argument appending callbacks */ ///@{ /** * Callback to append a bool argument to a message. */ static bool cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data) { assert(data); dbus_bool_t val = *(const bool *) data; if (!dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &val, DBUS_TYPE_INVALID)) { printf_errf("(): Failed to append argument."); return false; } return true; } /** * Callback to append an int32 argument to a message. */ static bool cdbus_apdarg_int32(session_t *ps, DBusMessage *msg, const void *data) { if (!dbus_message_append_args(msg, DBUS_TYPE_INT32, data, DBUS_TYPE_INVALID)) { printf_errf("(): Failed to append argument."); return false; } return true; } /** * Callback to append an uint32 argument to a message. */ static bool cdbus_apdarg_uint32(session_t *ps, DBusMessage *msg, const void *data) { if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, data, DBUS_TYPE_INVALID)) { printf_errf("(): Failed to append argument."); return false; } return true; } /** * Callback to append a double argument to a message. */ static bool cdbus_apdarg_double(session_t *ps, DBusMessage *msg, const void *data) { if (!dbus_message_append_args(msg, DBUS_TYPE_DOUBLE, data, DBUS_TYPE_INVALID)) { printf_errf("(): Failed to append argument."); return false; } return true; } /** * Callback to append a Window argument to a message. */ static bool cdbus_apdarg_wid(session_t *ps, DBusMessage *msg, const void *data) { assert(data); cdbus_window_t val = *(const Window *)data; if (!dbus_message_append_args(msg, CDBUS_TYPE_WINDOW, &val, DBUS_TYPE_INVALID)) { printf_errf("(): Failed to append argument."); return false; } return true; } /** * Callback to append an cdbus_enum_t argument to a message. */ static bool cdbus_apdarg_enum(session_t *ps, DBusMessage *msg, const void *data) { assert(data); if (!dbus_message_append_args(msg, CDBUS_TYPE_ENUM, data, DBUS_TYPE_INVALID)) { printf_errf("(): Failed to append argument."); return false; } return true; } /** * Callback to append a string argument to a message. */ static bool cdbus_apdarg_string(session_t *ps, DBusMessage *msg, const void *data) { const char *str = data; if (!str) str = ""; if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) { printf_errf("(): Failed to append argument."); return false; } return true; } /** * Callback to append all window IDs to a message. */ static bool cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data) { // Get the number of wids we are to include unsigned count = 0; for (win *w = ps->list; w; w = w->next) { if (!w->destroyed) ++count; } // Allocate memory for an array of window IDs cdbus_window_t *arr = malloc(sizeof(cdbus_window_t) * count); if (!arr) { printf_errf("(): Failed to allocate memory for window ID array."); return false; } // Build the array { cdbus_window_t *pcur = arr; for (win *w = ps->list; w; w = w->next) { if (!w->destroyed) { *pcur = w->id; ++pcur; assert(pcur <= arr + count); } } assert(pcur == arr + count); } // Append arguments if (!dbus_message_append_args(msg, DBUS_TYPE_ARRAY, CDBUS_TYPE_WINDOW, &arr, count, DBUS_TYPE_INVALID)) { printf_errf("(): Failed to append argument."); free(arr); return false; } free(arr); return true; } ///@} /** * Send a D-Bus signal. * * @param ps current session * @param name signal name * @param func a function that modifies the built message, to, for example, * add an argument * @param data data pointer to pass to the function */ static bool cdbus_signal(session_t *ps, const char *name, bool (*func)(session_t *ps, DBusMessage *msg, const void *data), const void *data) { DBusMessage* msg = NULL; // Create a signal msg = dbus_message_new_signal(CDBUS_OBJECT_NAME, CDBUS_INTERFACE_NAME, name); if (!msg) { printf_errf("(): Failed to create D-Bus signal."); return false; } // Append arguments onto message if (func && !func(ps, msg, data)) { dbus_message_unref(msg); return false; } // Send the message and flush the connection if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) { printf_errf("(): Failed to send D-Bus signal."); dbus_message_unref(msg); return false; } dbus_connection_flush(ps->dbus_conn); // Free the message dbus_message_unref(msg); return true; } /** * Send a D-Bus reply. * * @param ps current session * @param srcmsg original message * @param func a function that modifies the built message, to, for example, * add an argument * @param data data pointer to pass to the function */ static bool cdbus_reply(session_t *ps, DBusMessage *srcmsg, bool (*func)(session_t *ps, DBusMessage *msg, const void *data), const void *data) { DBusMessage* msg = NULL; // Create a reply msg = dbus_message_new_method_return(srcmsg); if (!msg) { printf_errf("(): Failed to create D-Bus reply."); return false; } // Append arguments onto message if (func && !func(ps, msg, data)) { dbus_message_unref(msg); return false; } // Send the message and flush the connection if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) { printf_errf("(): Failed to send D-Bus reply."); dbus_message_unref(msg); return false; } dbus_connection_flush(ps->dbus_conn); // Free the message dbus_message_unref(msg); return true; } /** * Send a D-Bus error reply. * * @param ps current session * @param msg the new error DBusMessage */ static bool cdbus_reply_errm(session_t *ps, DBusMessage *msg) { if (!msg) { printf_errf("(): Failed to create D-Bus reply."); return false; } // Send the message and flush the connection if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) { printf_errf("(): Failed to send D-Bus reply."); dbus_message_unref(msg); return false; } dbus_connection_flush(ps->dbus_conn); // Free the message dbus_message_unref(msg); return true; } /** * Get n-th argument of a D-Bus message. * * @param count the position of the argument to get, starting from 0 * @param type libdbus type number of the type * @param pdest pointer to the target * @return true if successful, false otherwise. */ static bool cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest) { assert(count >= 0); DBusMessageIter iter = { }; if (!dbus_message_iter_init(msg, &iter)) { printf_errf("(): Message has no argument."); return false; } { const int oldcount = count; while (count) { if (!dbus_message_iter_next(&iter)) { printf_errf("(): Failed to find argument %d.", oldcount); return false; } --count; } } if (type != dbus_message_iter_get_arg_type(&iter)) { printf_errf("(): Argument has incorrect type."); return false; } dbus_message_iter_get_basic(&iter, pdest); return true; } void cdbus_loop(session_t *ps) { dbus_connection_read_write(ps->dbus_conn, 0); DBusMessage *msg = NULL; while ((msg = dbus_connection_pop_message(ps->dbus_conn))) cdbus_process(ps, msg); } /** @name Message processing */ ///@{ /** * Process a message from D-Bus. */ static void cdbus_process(session_t *ps, DBusMessage *msg) { bool success = false; #define cdbus_m_ismethod(method) \ dbus_message_is_method_call(msg, CDBUS_INTERFACE_NAME, method) if (cdbus_m_ismethod("reset")) { ps->reset = true; if (!dbus_message_get_no_reply(msg)) cdbus_reply_bool(ps, msg, true); success = true; } else if (cdbus_m_ismethod("repaint")) { force_repaint(ps); if (!dbus_message_get_no_reply(msg)) cdbus_reply_bool(ps, msg, true); success = true; } else if (cdbus_m_ismethod("list_win")) { success = cdbus_process_list_win(ps, msg); } else if (cdbus_m_ismethod("win_get")) { success = cdbus_process_win_get(ps, msg); } else if (cdbus_m_ismethod("win_set")) { success = cdbus_process_win_set(ps, msg); } else if (cdbus_m_ismethod("find_win")) { success = cdbus_process_find_win(ps, msg); } else if (cdbus_m_ismethod("opts_get")) { success = cdbus_process_opts_get(ps, msg); } else if (cdbus_m_ismethod("opts_set")) { success = cdbus_process_opts_set(ps, msg); } #undef cdbus_m_ismethod else if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) { success = cdbus_process_introspect(ps, msg); } else if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Peer", "Ping")) { cdbus_reply(ps, msg, NULL, NULL); success = true; } else if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Peer", "GetMachineId")) { char *uuid = dbus_get_local_machine_id(); if (uuid) { cdbus_reply_string(ps, msg, uuid); dbus_free(uuid); success = true; } } else if (dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameAcquired") || dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameLost")) { success = true; } else { if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(msg)) { printf_errf("(): Error message of path \"%s\" " "interface \"%s\", member \"%s\", error \"%s\"", dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_error_name(msg)); } else { printf_errf("(): Illegal message of type \"%s\", path \"%s\" " "interface \"%s\", member \"%s\"", cdbus_repr_msgtype(msg), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg)); } if (DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg) && !dbus_message_get_no_reply(msg)) cdbus_reply_err(ps, msg, CDBUS_ERROR_BADMSG, CDBUS_ERROR_BADMSG_S); success = true; } // If the message could not be processed, and an reply is expected, return // an empty reply. if (!success && DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg) && !dbus_message_get_no_reply(msg)) cdbus_reply_err(ps, msg, CDBUS_ERROR_UNKNOWN, CDBUS_ERROR_UNKNOWN_S); // Free the message dbus_message_unref(msg); } /** * Process a list_win D-Bus request. */ static bool cdbus_process_list_win(session_t *ps, DBusMessage *msg) { cdbus_reply(ps, msg, cdbus_apdarg_wids, NULL); return true; } /** * Process a win_get D-Bus request. */ static bool cdbus_process_win_get(session_t *ps, DBusMessage *msg) { cdbus_window_t wid = None; const char *target = NULL; DBusError err = { }; if (!dbus_message_get_args(msg, &err, CDBUS_TYPE_WINDOW, &wid, DBUS_TYPE_STRING, &target, DBUS_TYPE_INVALID)) { printf_errf("(): Failed to parse argument of \"win_get\" (%s).", err.message); dbus_error_free(&err); return false; } win *w = find_win(ps, wid); if (!w) { printf_errf("(): Window %#010x not found.", wid); cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid); return true; } #define cdbus_m_win_get_do(tgt, apdarg_func) \ if (!strcmp(MSTR(tgt), target)) { \ apdarg_func(ps, msg, w->tgt); \ return true; \ } cdbus_m_win_get_do(id, cdbus_reply_wid); // next if (!strcmp("next", target)) { cdbus_reply_wid(ps, msg, (w->next ? w->next->id: 0)); return true; } // map_state if (!strcmp("map_state", target)) { cdbus_reply_bool(ps, msg, w->a.map_state); return true; } cdbus_m_win_get_do(mode, cdbus_reply_enum); cdbus_m_win_get_do(client_win, cdbus_reply_wid); cdbus_m_win_get_do(damaged, cdbus_reply_bool); cdbus_m_win_get_do(destroyed, cdbus_reply_bool); cdbus_m_win_get_do(window_type, cdbus_reply_enum); cdbus_m_win_get_do(wmwin, cdbus_reply_bool); cdbus_m_win_get_do(leader, cdbus_reply_wid); // focused_real if (!strcmp("focused_real", target)) { cdbus_reply_bool(ps, msg, win_is_focused_real(ps, w)); return true; } cdbus_m_win_get_do(fade_force, cdbus_reply_enum); cdbus_m_win_get_do(shadow_force, cdbus_reply_enum); cdbus_m_win_get_do(focused_force, cdbus_reply_enum); cdbus_m_win_get_do(invert_color_force, cdbus_reply_enum); cdbus_m_win_get_do(name, cdbus_reply_string); cdbus_m_win_get_do(class_instance, cdbus_reply_string); cdbus_m_win_get_do(class_general, cdbus_reply_string); cdbus_m_win_get_do(role, cdbus_reply_string); cdbus_m_win_get_do(opacity, cdbus_reply_uint32); cdbus_m_win_get_do(opacity_tgt, cdbus_reply_uint32); cdbus_m_win_get_do(opacity_prop, cdbus_reply_uint32); cdbus_m_win_get_do(opacity_prop_client, cdbus_reply_uint32); cdbus_m_win_get_do(opacity_set, cdbus_reply_uint32); cdbus_m_win_get_do(frame_opacity, cdbus_reply_double); if (!strcmp("left_width", target)) { cdbus_reply_uint32(ps, msg, w->frame_extents.left); return true; } if (!strcmp("right_width", target)) { cdbus_reply_uint32(ps, msg, w->frame_extents.right); return true; } if (!strcmp("top_width", target)) { cdbus_reply_uint32(ps, msg, w->frame_extents.top); return true; } if (!strcmp("bottom_width", target)) { cdbus_reply_uint32(ps, msg, w->frame_extents.bottom); return true; } cdbus_m_win_get_do(shadow, cdbus_reply_bool); cdbus_m_win_get_do(fade, cdbus_reply_bool); cdbus_m_win_get_do(invert_color, cdbus_reply_bool); cdbus_m_win_get_do(blur_background, cdbus_reply_bool); #undef cdbus_m_win_get_do printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); return true; } /** * Process a win_set D-Bus request. */ static bool cdbus_process_win_set(session_t *ps, DBusMessage *msg) { cdbus_window_t wid = None; const char *target = NULL; DBusError err = { }; if (!dbus_message_get_args(msg, &err, CDBUS_TYPE_WINDOW, &wid, DBUS_TYPE_STRING, &target, DBUS_TYPE_INVALID)) { printf_errf("(): Failed to parse argument of \"win_set\" (%s).", err.message); dbus_error_free(&err); return false; } win *w = find_win(ps, wid); if (!w) { printf_errf("(): Window %#010x not found.", wid); cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid); return true; } #define cdbus_m_win_set_do(tgt, type, real_type) \ if (!strcmp(MSTR(tgt), target)) { \ real_type val; \ if (!cdbus_msg_get_arg(msg, 2, type, &val)) \ return false; \ w->tgt = val; \ goto cdbus_process_win_set_success; \ } if (!strcmp("shadow_force", target)) { cdbus_enum_t val = UNSET; if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) return false; win_set_shadow_force(ps, w, val); goto cdbus_process_win_set_success; } if (!strcmp("fade_force", target)) { cdbus_enum_t val = UNSET; if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) return false; win_set_fade_force(ps, w, val); goto cdbus_process_win_set_success; } if (!strcmp("focused_force", target)) { cdbus_enum_t val = UNSET; if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) return false; win_set_focused_force(ps, w, val); goto cdbus_process_win_set_success; } if (!strcmp("invert_color_force", target)) { cdbus_enum_t val = UNSET; if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) return false; win_set_invert_color_force(ps, w, val); goto cdbus_process_win_set_success; } #undef cdbus_m_win_set_do printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); return true; cdbus_process_win_set_success: if (!dbus_message_get_no_reply(msg)) cdbus_reply_bool(ps, msg, true); return true; } /** * Process a find_win D-Bus request. */ static bool cdbus_process_find_win(session_t *ps, DBusMessage *msg) { const char *target = NULL; if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) return false; Window wid = None; // Find window by client window if (!strcmp("client", target)) { cdbus_window_t client = None; if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_WINDOW, &client)) return false; win *w = find_toplevel(ps, client); if (w) wid = w->id; } // Find focused window else if (!strcmp("focused", target)) { win *w = find_focused(ps); if (w) wid = w->id; } else { printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); return true; } cdbus_reply_wid(ps, msg, wid); return true; } /** * Process a opts_get D-Bus request. */ static bool cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { const char *target = NULL; if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) return false; #define cdbus_m_opts_get_do(tgt, apdarg_func) \ if (!strcmp(MSTR(tgt), target)) { \ apdarg_func(ps, msg, ps->o.tgt); \ return true; \ } // version if (!strcmp("version", target)) { cdbus_reply_string(ps, msg, COMPTON_VERSION); return true; } // pid if (!strcmp("pid", target)) { cdbus_reply_int32(ps, msg, getpid()); return true; } // display if (!strcmp("display", target)) { cdbus_reply_string(ps, msg, DisplayString(ps->dpy)); return true; } cdbus_m_opts_get_do(config_file, cdbus_reply_string); cdbus_m_opts_get_do(display_repr, cdbus_reply_string); cdbus_m_opts_get_do(write_pid_path, cdbus_reply_string); cdbus_m_opts_get_do(mark_wmwin_focused, cdbus_reply_bool); cdbus_m_opts_get_do(mark_ovredir_focused, cdbus_reply_bool); cdbus_m_opts_get_do(fork_after_register, cdbus_reply_bool); cdbus_m_opts_get_do(detect_rounded_corners, cdbus_reply_bool); cdbus_m_opts_get_do(paint_on_overlay, cdbus_reply_bool); // paint_on_overlay_id: Get ID of the X composite overlay window if (!strcmp("paint_on_overlay_id", target)) { cdbus_reply_uint32(ps, msg, ps->overlay); return true; } cdbus_m_opts_get_do(unredir_if_possible, cdbus_reply_bool); cdbus_m_opts_get_do(unredir_if_possible_delay, cdbus_reply_int32); cdbus_m_opts_get_do(redirected_force, cdbus_reply_enum); cdbus_m_opts_get_do(stoppaint_force, cdbus_reply_enum); cdbus_m_opts_get_do(logpath, cdbus_reply_string); cdbus_m_opts_get_do(synchronize, cdbus_reply_bool); cdbus_m_opts_get_do(refresh_rate, cdbus_reply_int32); cdbus_m_opts_get_do(sw_opti, cdbus_reply_bool); if (!strcmp("vsync", target)) { assert(ps->o.vsync < sizeof(VSYNC_STRS) / sizeof(VSYNC_STRS[0])); cdbus_reply_string(ps, msg, VSYNC_STRS[ps->o.vsync]); return true; } if (!strcmp("backend", target)) { assert(ps->o.backend < sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0])); cdbus_reply_string(ps, msg, BACKEND_STRS[ps->o.backend]); return true; } cdbus_m_opts_get_do(dbe, cdbus_reply_bool); cdbus_m_opts_get_do(vsync_aggressive, cdbus_reply_bool); cdbus_m_opts_get_do(shadow_red, cdbus_reply_double); cdbus_m_opts_get_do(shadow_green, cdbus_reply_double); cdbus_m_opts_get_do(shadow_blue, cdbus_reply_double); cdbus_m_opts_get_do(shadow_radius, cdbus_reply_int32); cdbus_m_opts_get_do(shadow_offset_x, cdbus_reply_int32); cdbus_m_opts_get_do(shadow_offset_y, cdbus_reply_int32); cdbus_m_opts_get_do(shadow_opacity, cdbus_reply_double); cdbus_m_opts_get_do(clear_shadow, cdbus_reply_bool); cdbus_m_opts_get_do(xinerama_shadow_crop, cdbus_reply_bool); cdbus_m_opts_get_do(fade_delta, cdbus_reply_int32); cdbus_m_opts_get_do(fade_in_step, cdbus_reply_int32); cdbus_m_opts_get_do(fade_out_step, cdbus_reply_int32); cdbus_m_opts_get_do(no_fading_openclose, cdbus_reply_bool); cdbus_m_opts_get_do(blur_background, cdbus_reply_bool); cdbus_m_opts_get_do(blur_background_frame, cdbus_reply_bool); cdbus_m_opts_get_do(blur_background_fixed, cdbus_reply_bool); cdbus_m_opts_get_do(inactive_dim, cdbus_reply_double); cdbus_m_opts_get_do(inactive_dim_fixed, cdbus_reply_bool); cdbus_m_opts_get_do(use_ewmh_active_win, cdbus_reply_bool); cdbus_m_opts_get_do(detect_transient, cdbus_reply_bool); cdbus_m_opts_get_do(detect_client_leader, cdbus_reply_bool); #ifdef CONFIG_VSYNC_OPENGL cdbus_m_opts_get_do(glx_no_stencil, cdbus_reply_bool); cdbus_m_opts_get_do(glx_copy_from_front, cdbus_reply_bool); cdbus_m_opts_get_do(glx_use_copysubbuffermesa, cdbus_reply_bool); cdbus_m_opts_get_do(glx_no_rebind_pixmap, cdbus_reply_bool); cdbus_m_opts_get_do(glx_swap_method, cdbus_reply_int32); #endif cdbus_m_opts_get_do(track_focus, cdbus_reply_bool); cdbus_m_opts_get_do(track_wdata, cdbus_reply_bool); cdbus_m_opts_get_do(track_leader, cdbus_reply_bool); #undef cdbus_m_opts_get_do printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); return true; } /** * Process a opts_set D-Bus request. */ static bool cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { const char *target = NULL; if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) return false; #define cdbus_m_opts_set_do(tgt, type, real_type) \ if (!strcmp(MSTR(tgt), target)) { \ real_type val; \ if (!cdbus_msg_get_arg(msg, 1, type, &val)) \ return false; \ ps->o.tgt = val; \ goto cdbus_process_opts_set_success; \ } // fade_delta if (!strcmp("fade_delta", target)) { int32_t val = 0.0; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_INT32, &val)) return false; ps->o.fade_delta = max_i(val, 1); goto cdbus_process_opts_set_success; } // fade_in_step if (!strcmp("fade_in_step", target)) { double val = 0.0; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val)) return false; ps->o.fade_in_step = normalize_d(val) * OPAQUE; goto cdbus_process_opts_set_success; } // fade_out_step if (!strcmp("fade_out_step", target)) { double val = 0.0; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val)) return false; ps->o.fade_out_step = normalize_d(val) * OPAQUE; goto cdbus_process_opts_set_success; } // no_fading_openclose if (!strcmp("no_fading_openclose", target)) { dbus_bool_t val = FALSE; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) return false; opts_set_no_fading_openclose(ps, val); goto cdbus_process_opts_set_success; } // unredir_if_possible if (!strcmp("unredir_if_possible", target)) { dbus_bool_t val = FALSE; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) return false; if (ps->o.unredir_if_possible != val) { ps->o.unredir_if_possible = val; ps->ev_received = true; } goto cdbus_process_opts_set_success; } // clear_shadow if (!strcmp("clear_shadow", target)) { dbus_bool_t val = FALSE; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) return false; if (ps->o.clear_shadow != val) { ps->o.clear_shadow = val; force_repaint(ps); } goto cdbus_process_opts_set_success; } // track_focus if (!strcmp("track_focus", target)) { dbus_bool_t val = FALSE; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) return false; // You could enable this option, but never turn if off if (val) { opts_init_track_focus(ps); } goto cdbus_process_opts_set_success; } // vsync if (!strcmp("vsync", target)) { const char * val = NULL; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_STRING, &val)) return false; vsync_deinit(ps); if (!parse_vsync(ps, val)) { printf_errf("(): " CDBUS_ERROR_BADARG_S, 1, "Value invalid."); cdbus_reply_err(ps, msg, CDBUS_ERROR_BADARG, CDBUS_ERROR_BADARG_S, 1, "Value invalid."); } else if (!vsync_init(ps)) { printf_errf("(): " CDBUS_ERROR_CUSTOM_S, "Failed to initialize specified VSync method."); cdbus_reply_err(ps, msg, CDBUS_ERROR_CUSTOM, CDBUS_ERROR_CUSTOM_S, "Failed to initialize specified VSync method."); } else goto cdbus_process_opts_set_success; return true; } // redirected_force if (!strcmp("redirected_force", target)) { cdbus_enum_t val = UNSET; if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_ENUM, &val)) return false; ps->o.redirected_force = val; force_repaint(ps); goto cdbus_process_opts_set_success; } // stoppaint_force cdbus_m_opts_set_do(stoppaint_force, CDBUS_TYPE_ENUM, cdbus_enum_t); #undef cdbus_m_opts_set_do printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); return true; cdbus_process_opts_set_success: if (!dbus_message_get_no_reply(msg)) cdbus_reply_bool(ps, msg, true); return true; } /** * Process an Introspect D-Bus request. */ static bool cdbus_process_introspect(session_t *ps, DBusMessage *msg) { const static char *str_introspect = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n"; cdbus_reply_string(ps, msg, str_introspect); return true; } ///@} /** @name Core callbacks */ ///@{ void cdbus_ev_win_added(session_t *ps, win *w) { if (ps->dbus_conn) cdbus_signal_wid(ps, "win_added", w->id); } void cdbus_ev_win_destroyed(session_t *ps, win *w) { if (ps->dbus_conn) cdbus_signal_wid(ps, "win_destroyed", w->id); } void cdbus_ev_win_mapped(session_t *ps, win *w) { if (ps->dbus_conn) cdbus_signal_wid(ps, "win_mapped", w->id); } void cdbus_ev_win_unmapped(session_t *ps, win *w) { if (ps->dbus_conn) cdbus_signal_wid(ps, "win_unmapped", w->id); } void cdbus_ev_win_focusout(session_t *ps, win *w) { if (ps->dbus_conn) cdbus_signal_wid(ps, "win_focusout", w->id); } void cdbus_ev_win_focusin(session_t *ps, win *w) { if (ps->dbus_conn) cdbus_signal_wid(ps, "win_focusin", w->id); } //!@} compton-0.1~beta2+20150922/src/dbus.h000066400000000000000000000151731260012102400167350ustar00rootroot00000000000000/* * Compton - a compositor for X11 * * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * * Copyright (c) 2011-2013, Christopher Jeffrey * See LICENSE for more information. * */ #include "common.h" #include #include #include #define CDBUS_SERVICE_NAME "com.github.chjj.compton" #define CDBUS_INTERFACE_NAME CDBUS_SERVICE_NAME #define CDBUS_OBJECT_NAME "/com/github/chjj/compton" #define CDBUS_ERROR_PREFIX CDBUS_INTERFACE_NAME ".error" #define CDBUS_ERROR_UNKNOWN CDBUS_ERROR_PREFIX ".unknown" #define CDBUS_ERROR_UNKNOWN_S "Well, I don't know what happened. Do you?" #define CDBUS_ERROR_BADMSG CDBUS_ERROR_PREFIX ".bad_message" #define CDBUS_ERROR_BADMSG_S "Unrecognized command. Beware compton " \ "cannot make you a sandwich." #define CDBUS_ERROR_BADARG CDBUS_ERROR_PREFIX ".bad_argument" #define CDBUS_ERROR_BADARG_S "Failed to parse argument %d: %s" #define CDBUS_ERROR_BADWIN CDBUS_ERROR_PREFIX ".bad_window" #define CDBUS_ERROR_BADWIN_S "Requested window %#010lx not found." #define CDBUS_ERROR_BADTGT CDBUS_ERROR_PREFIX ".bad_target" #define CDBUS_ERROR_BADTGT_S "Target \"%s\" not found." #define CDBUS_ERROR_FORBIDDEN CDBUS_ERROR_PREFIX ".forbidden" #define CDBUS_ERROR_FORBIDDEN_S "Incorrect password, access denied." #define CDBUS_ERROR_CUSTOM CDBUS_ERROR_PREFIX ".custom" #define CDBUS_ERROR_CUSTOM_S "%s" // Window type typedef uint32_t cdbus_window_t; #define CDBUS_TYPE_WINDOW DBUS_TYPE_UINT32 #define CDBUS_TYPE_WINDOW_STR DBUS_TYPE_UINT32_AS_STRING typedef uint16_t cdbus_enum_t; #define CDBUS_TYPE_ENUM DBUS_TYPE_UINT16 #define CDBUS_TYPE_ENUM_STR DBUS_TYPE_UINT16_AS_STRING static dbus_bool_t cdbus_callback_add_timeout(DBusTimeout *timeout, void *data); static void cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data); static void cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data); static bool cdbus_callback_handle_timeout(session_t *ps, timeout_t *ptmout); /** * Determine the poll condition of a DBusWatch. */ static inline short cdbus_get_watch_cond(DBusWatch *watch) { const unsigned flags = dbus_watch_get_flags(watch); short condition = POLLERR | POLLHUP; if (flags & DBUS_WATCH_READABLE) condition |= POLLIN; if (flags & DBUS_WATCH_WRITABLE) condition |= POLLOUT; return condition; } static dbus_bool_t cdbus_callback_add_watch(DBusWatch *watch, void *data); static void cdbus_callback_remove_watch(DBusWatch *watch, void *data); static void cdbus_callback_watch_toggled(DBusWatch *watch, void *data); static bool cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data); static bool cdbus_apdarg_int32(session_t *ps, DBusMessage *msg, const void *data); static bool cdbus_apdarg_uint32(session_t *ps, DBusMessage *msg, const void *data); static bool cdbus_apdarg_double(session_t *ps, DBusMessage *msg, const void *data); static bool cdbus_apdarg_wid(session_t *ps, DBusMessage *msg, const void *data); static bool cdbus_apdarg_enum(session_t *ps, DBusMessage *msg, const void *data); static bool cdbus_apdarg_string(session_t *ps, DBusMessage *msg, const void *data); static bool cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data); /** @name DBus signal sending */ ///@{ static bool cdbus_signal(session_t *ps, const char *name, bool (*func)(session_t *ps, DBusMessage *msg, const void *data), const void *data); /** * Send a signal with no argument. */ static inline bool cdbus_signal_noarg(session_t *ps, const char *name) { return cdbus_signal(ps, name, NULL, NULL); } /** * Send a signal with a Window ID as argument. */ static inline bool cdbus_signal_wid(session_t *ps, const char *name, Window wid) { return cdbus_signal(ps, name, cdbus_apdarg_wid, &wid); } ///@} /** @name DBus reply sending */ ///@{ static bool cdbus_reply(session_t *ps, DBusMessage *srcmsg, bool (*func)(session_t *ps, DBusMessage *msg, const void *data), const void *data); static bool cdbus_reply_errm(session_t *ps, DBusMessage *msg); #define cdbus_reply_err(ps, srcmsg, err_name, err_format, ...) \ cdbus_reply_errm((ps), dbus_message_new_error_printf((srcmsg), (err_name), (err_format), ## __VA_ARGS__)) /** * Send a reply with no argument. */ static inline bool cdbus_reply_noarg(session_t *ps, DBusMessage *srcmsg) { return cdbus_reply(ps, srcmsg, NULL, NULL); } /** * Send a reply with a bool argument. */ static inline bool cdbus_reply_bool(session_t *ps, DBusMessage *srcmsg, bool bval) { return cdbus_reply(ps, srcmsg, cdbus_apdarg_bool, &bval); } /** * Send a reply with an int32 argument. */ static inline bool cdbus_reply_int32(session_t *ps, DBusMessage *srcmsg, int32_t val) { return cdbus_reply(ps, srcmsg, cdbus_apdarg_int32, &val); } /** * Send a reply with an uint32 argument. */ static inline bool cdbus_reply_uint32(session_t *ps, DBusMessage *srcmsg, uint32_t val) { return cdbus_reply(ps, srcmsg, cdbus_apdarg_uint32, &val); } /** * Send a reply with a double argument. */ static inline bool cdbus_reply_double(session_t *ps, DBusMessage *srcmsg, double val) { return cdbus_reply(ps, srcmsg, cdbus_apdarg_double, &val); } /** * Send a reply with a wid argument. */ static inline bool cdbus_reply_wid(session_t *ps, DBusMessage *srcmsg, Window wid) { return cdbus_reply(ps, srcmsg, cdbus_apdarg_wid, &wid); } /** * Send a reply with a string argument. */ static inline bool cdbus_reply_string(session_t *ps, DBusMessage *srcmsg, const char *str) { return cdbus_reply(ps, srcmsg, cdbus_apdarg_string, str); } /** * Send a reply with a enum argument. */ static inline bool cdbus_reply_enum(session_t *ps, DBusMessage *srcmsg, cdbus_enum_t eval) { return cdbus_reply(ps, srcmsg, cdbus_apdarg_enum, &eval); } ///@} static bool cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest); /** * Return a string representation of a D-Bus message type. */ static inline const char * cdbus_repr_msgtype(DBusMessage *msg) { return dbus_message_type_to_string(dbus_message_get_type(msg)); } /** @name Message processing */ ///@{ static void cdbus_process(session_t *ps, DBusMessage *msg); static bool cdbus_process_list_win(session_t *ps, DBusMessage *msg); static bool cdbus_process_win_get(session_t *ps, DBusMessage *msg); static bool cdbus_process_win_set(session_t *ps, DBusMessage *msg); static bool cdbus_process_find_win(session_t *ps, DBusMessage *msg); static bool cdbus_process_opts_get(session_t *ps, DBusMessage *msg); static bool cdbus_process_opts_set(session_t *ps, DBusMessage *msg); static bool cdbus_process_introspect(session_t *ps, DBusMessage *msg); ///@} compton-0.1~beta2+20150922/src/opengl.c000066400000000000000000001533711260012102400172620ustar00rootroot00000000000000/* * Compton - a compositor for X11 * * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * * Copyright (c) 2011-2013, Christopher Jeffrey * See LICENSE for more information. * */ #include "opengl.h" #ifdef CONFIG_GLX_SYNC void xr_glx_sync(session_t *ps, Drawable d, XSyncFence *pfence) { if (*pfence) { // GLsync sync = ps->psglx->glFenceSyncProc(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLsync sync = ps->psglx->glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, *pfence, 0); /* GLenum ret = ps->psglx->glClientWaitSyncProc(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000); assert(GL_CONDITION_SATISFIED == ret); */ XSyncTriggerFence(ps->dpy, *pfence); XFlush(ps->dpy); ps->psglx->glWaitSyncProc(sync, 0, GL_TIMEOUT_IGNORED); // ps->psglx->glDeleteSyncProc(sync); // XSyncResetFence(ps->dpy, *pfence); } glx_check_err(ps); } #endif static inline GLXFBConfig get_fbconfig_from_visualinfo(session_t *ps, const XVisualInfo *visualinfo) { int nelements = 0; GLXFBConfig *fbconfigs = glXGetFBConfigs(ps->dpy, visualinfo->screen, &nelements); for (int i = 0; i < nelements; ++i) { int visual_id = 0; if (Success == glXGetFBConfigAttrib(ps->dpy, fbconfigs[i], GLX_VISUAL_ID, &visual_id) && visual_id == visualinfo->visualid) return fbconfigs[i]; } return NULL; } #ifdef DEBUG_GLX_DEBUG_CONTEXT static void glx_debug_msg_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, GLvoid *userParam) { printf_dbgf("(): source 0x%04X, type 0x%04X, id %u, severity 0x%0X, \"%s\"\n", source, type, id, severity, message); } #endif /** * Initialize OpenGL. */ bool glx_init(session_t *ps, bool need_render) { bool success = false; XVisualInfo *pvis = NULL; // Check for GLX extension if (!ps->glx_exists) { if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error)) ps->glx_exists = true; else { printf_errf("(): No GLX extension."); goto glx_init_end; } } // Get XVisualInfo pvis = get_visualinfo_from_visual(ps, ps->vis); if (!pvis) { printf_errf("(): Failed to acquire XVisualInfo for current visual."); goto glx_init_end; } // Ensure the visual is double-buffered if (need_render) { int value = 0; if (Success != glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) { printf_errf("(): Root visual is not a GL visual."); goto glx_init_end; } if (Success != glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) { printf_errf("(): Root visual is not a double buffered GL visual."); goto glx_init_end; } } // Ensure GLX_EXT_texture_from_pixmap exists if (need_render && !glx_hasglxext(ps, "GLX_EXT_texture_from_pixmap")) goto glx_init_end; // Initialize GLX data structure if (!ps->psglx) { static const glx_session_t CGLX_SESSION_DEF = CGLX_SESSION_INIT; ps->psglx = cmalloc(1, glx_session_t); memcpy(ps->psglx, &CGLX_SESSION_DEF, sizeof(glx_session_t)); #ifdef CONFIG_VSYNC_OPENGL_GLSL for (int i = 0; i < MAX_BLUR_PASS; ++i) { glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; ppass->unifm_factor_center = -1; ppass->unifm_offset_x = -1; ppass->unifm_offset_y = -1; } #endif } glx_session_t *psglx = ps->psglx; if (!psglx->context) { // Get GLX context #ifndef DEBUG_GLX_DEBUG_CONTEXT psglx->context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); #else { GLXFBConfig fbconfig = get_fbconfig_from_visualinfo(ps, pvis); if (!fbconfig) { printf_errf("(): Failed to get GLXFBConfig for root visual %#lx.", pvis->visualid); goto glx_init_end; } f_glXCreateContextAttribsARB p_glXCreateContextAttribsARB = (f_glXCreateContextAttribsARB) glXGetProcAddress((const GLubyte *) "glXCreateContextAttribsARB"); if (!p_glXCreateContextAttribsARB) { printf_errf("(): Failed to get glXCreateContextAttribsARB()."); goto glx_init_end; } static const int attrib_list[] = { GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, None }; psglx->context = p_glXCreateContextAttribsARB(ps->dpy, fbconfig, NULL, GL_TRUE, attrib_list); } #endif if (!psglx->context) { printf_errf("(): Failed to get GLX context."); goto glx_init_end; } // Attach GLX context if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), psglx->context)) { printf_errf("(): Failed to attach GLX context."); goto glx_init_end; } #ifdef DEBUG_GLX_DEBUG_CONTEXT { f_DebugMessageCallback p_DebugMessageCallback = (f_DebugMessageCallback) glXGetProcAddress((const GLubyte *) "glDebugMessageCallback"); if (!p_DebugMessageCallback) { printf_errf("(): Failed to get glDebugMessageCallback(0."); goto glx_init_end; } p_DebugMessageCallback(glx_debug_msg_callback, ps); } #endif } // Ensure we have a stencil buffer. X Fixes does not guarantee rectangles // in regions don't overlap, so we must use stencil buffer to make sure // we don't paint a region for more than one time, I think? if (need_render && !ps->o.glx_no_stencil) { GLint val = 0; glGetIntegerv(GL_STENCIL_BITS, &val); if (!val) { printf_errf("(): Target window doesn't have stencil buffer."); goto glx_init_end; } } // Check GL_ARB_texture_non_power_of_two, requires a GLX context and // must precede FBConfig fetching if (need_render) psglx->has_texture_non_power_of_two = glx_hasglext(ps, "GL_ARB_texture_non_power_of_two"); // Acquire function addresses if (need_render) { #ifdef DEBUG_GLX_MARK psglx->glStringMarkerGREMEDY = (f_StringMarkerGREMEDY) glXGetProcAddress((const GLubyte *) "glStringMarkerGREMEDY"); psglx->glFrameTerminatorGREMEDY = (f_FrameTerminatorGREMEDY) glXGetProcAddress((const GLubyte *) "glFrameTerminatorGREMEDY"); #endif psglx->glXBindTexImageProc = (f_BindTexImageEXT) glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT"); psglx->glXReleaseTexImageProc = (f_ReleaseTexImageEXT) glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT"); if (!psglx->glXBindTexImageProc || !psglx->glXReleaseTexImageProc) { printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT()."); goto glx_init_end; } if (ps->o.glx_use_copysubbuffermesa) { psglx->glXCopySubBufferProc = (f_CopySubBuffer) glXGetProcAddress((const GLubyte *) "glXCopySubBufferMESA"); if (!psglx->glXCopySubBufferProc) { printf_errf("(): Failed to acquire glXCopySubBufferMESA()."); goto glx_init_end; } } #ifdef CONFIG_GLX_SYNC psglx->glFenceSyncProc = (f_FenceSync) glXGetProcAddress((const GLubyte *) "glFenceSync"); psglx->glIsSyncProc = (f_IsSync) glXGetProcAddress((const GLubyte *) "glIsSync"); psglx->glDeleteSyncProc = (f_DeleteSync) glXGetProcAddress((const GLubyte *) "glDeleteSync"); psglx->glClientWaitSyncProc = (f_ClientWaitSync) glXGetProcAddress((const GLubyte *) "glClientWaitSync"); psglx->glWaitSyncProc = (f_WaitSync) glXGetProcAddress((const GLubyte *) "glWaitSync"); psglx->glImportSyncEXT = (f_ImportSyncEXT) glXGetProcAddress((const GLubyte *) "glImportSyncEXT"); if (!psglx->glFenceSyncProc || !psglx->glIsSyncProc || !psglx->glDeleteSyncProc || !psglx->glClientWaitSyncProc || !psglx->glWaitSyncProc || !psglx->glImportSyncEXT) { printf_errf("(): Failed to acquire GLX sync functions."); goto glx_init_end; } #endif } // Acquire FBConfigs if (need_render && !glx_update_fbconfig(ps)) goto glx_init_end; // Render preparations if (need_render) { glx_on_root_change(ps); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_BLEND); if (!ps->o.glx_no_stencil) { // Initialize stencil buffer glClear(GL_STENCIL_BUFFER_BIT); glDisable(GL_STENCIL_TEST); glStencilMask(0x1); glStencilFunc(GL_EQUAL, 0x1, 0x1); } // Clear screen glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // glXSwapBuffers(ps->dpy, get_tgt_window(ps)); } success = true; glx_init_end: cxfree(pvis); if (!success) glx_destroy(ps); return success; } #ifdef CONFIG_VSYNC_OPENGL_GLSL static void glx_free_prog_main(session_t *ps, glx_prog_main_t *pprogram) { if (!pprogram) return; if (pprogram->prog) { glDeleteProgram(pprogram->prog); pprogram->prog = 0; } pprogram->unifm_opacity = -1; pprogram->unifm_invert_color = -1; pprogram->unifm_tex = -1; } #endif /** * Destroy GLX related resources. */ void glx_destroy(session_t *ps) { if (!ps->psglx) return; // Free all GLX resources of windows for (win *w = ps->list; w; w = w->next) free_win_res_glx(ps, w); #ifdef CONFIG_VSYNC_OPENGL_GLSL // Free GLSL shaders/programs for (int i = 0; i < MAX_BLUR_PASS; ++i) { glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; if (ppass->frag_shader) glDeleteShader(ppass->frag_shader); if (ppass->prog) glDeleteProgram(ppass->prog); } glx_free_prog_main(ps, &ps->o.glx_prog_win); glx_check_err(ps); #endif // Free FBConfigs for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) { free(ps->psglx->fbconfigs[i]); ps->psglx->fbconfigs[i] = NULL; } // Destroy GLX context if (ps->psglx->context) { glXDestroyContext(ps->dpy, ps->psglx->context); ps->psglx->context = NULL; } free(ps->psglx); ps->psglx = NULL; } /** * Reinitialize GLX. */ bool glx_reinit(session_t *ps, bool need_render) { // Reinitialize VSync as well vsync_deinit(ps); glx_destroy(ps); if (!glx_init(ps, need_render)) { printf_errf("(): Failed to initialize GLX."); return false; } if (!vsync_init(ps)) { printf_errf("(): Failed to initialize VSync."); return false; } return true; } /** * Callback to run on root window size change. */ void glx_on_root_change(session_t *ps) { glViewport(0, 0, ps->root_width, ps->root_height); // Initialize matrix, copied from dcompmgr glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, ps->root_width, 0, ps->root_height, -1000.0, 1000.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } /** * Initialize GLX blur filter. */ bool glx_init_blur(session_t *ps) { assert(ps->o.blur_kerns[0]); // Allocate PBO if more than one blur kernel is present if (ps->o.blur_kerns[1]) { #ifdef CONFIG_VSYNC_OPENGL_FBO // Try to generate a framebuffer GLuint fbo = 0; glGenFramebuffers(1, &fbo); if (!fbo) { printf_errf("(): Failed to generate Framebuffer. Cannot do " "multi-pass blur with GLX backend."); return false; } glDeleteFramebuffers(1, &fbo); #else printf_errf("(): FBO support not compiled in. Cannot do multi-pass blur " "with GLX backend."); return false; #endif } #ifdef CONFIG_VSYNC_OPENGL_GLSL { char *lc_numeric_old = mstrcpy(setlocale(LC_NUMERIC, NULL)); // Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane // Thanks to hiciu for reporting. setlocale(LC_NUMERIC, "C"); static const char *FRAG_SHADER_BLUR_PREFIX = "#version 110\n" "%s" "uniform float offset_x;\n" "uniform float offset_y;\n" "uniform float factor_center;\n" "uniform %s tex_scr;\n" "\n" "void main() {\n" " vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n"; static const char *FRAG_SHADER_BLUR_ADD = " sum += float(%.7g) * %s(tex_scr, vec2(gl_TexCoord[0].x + offset_x * float(%d), gl_TexCoord[0].y + offset_y * float(%d)));\n"; static const char *FRAG_SHADER_BLUR_ADD_GPUSHADER4 = " sum += float(%.7g) * %sOffset(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y), ivec2(%d, %d));\n"; static const char *FRAG_SHADER_BLUR_SUFFIX = " sum += %s(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * factor_center;\n" " gl_FragColor = sum / (factor_center + float(%.7g));\n" "}\n"; const bool use_texture_rect = !ps->psglx->has_texture_non_power_of_two; const char *sampler_type = (use_texture_rect ? "sampler2DRect": "sampler2D"); const char *texture_func = (use_texture_rect ? "texture2DRect": "texture2D"); const char *shader_add = FRAG_SHADER_BLUR_ADD; char *extension = mstrcpy(""); if (use_texture_rect) mstrextend(&extension, "#extension GL_ARB_texture_rectangle : require\n"); if (ps->o.glx_use_gpushader4) { mstrextend(&extension, "#extension GL_EXT_gpu_shader4 : require\n"); shader_add = FRAG_SHADER_BLUR_ADD_GPUSHADER4; } for (int i = 0; i < MAX_BLUR_PASS && ps->o.blur_kerns[i]; ++i) { XFixed *kern = ps->o.blur_kerns[i]; if (!kern) break; glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; // Build shader { int wid = XFixedToDouble(kern[0]), hei = XFixedToDouble(kern[1]); int nele = wid * hei - 1; int len = strlen(FRAG_SHADER_BLUR_PREFIX) + strlen(sampler_type) + strlen(extension) + (strlen(shader_add) + strlen(texture_func) + 42) * nele + strlen(FRAG_SHADER_BLUR_SUFFIX) + strlen(texture_func) + 12 + 1; char *shader_str = calloc(len, sizeof(char)); if (!shader_str) { printf_errf("(): Failed to allocate %d bytes for shader string.", len); return false; } { char *pc = shader_str; sprintf(pc, FRAG_SHADER_BLUR_PREFIX, extension, sampler_type); pc += strlen(pc); assert(strlen(shader_str) < len); double sum = 0.0; for (int j = 0; j < hei; ++j) { for (int k = 0; k < wid; ++k) { if (hei / 2 == j && wid / 2 == k) continue; double val = XFixedToDouble(kern[2 + j * wid + k]); if (0.0 == val) continue; sum += val; sprintf(pc, shader_add, val, texture_func, k - wid / 2, j - hei / 2); pc += strlen(pc); assert(strlen(shader_str) < len); } } sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, texture_func, sum); assert(strlen(shader_str) < len); } ppass->frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, shader_str); free(shader_str); } if (!ppass->frag_shader) { printf_errf("(): Failed to create fragment shader %d.", i); return false; } // Build program ppass->prog = glx_create_program(&ppass->frag_shader, 1); if (!ppass->prog) { printf_errf("(): Failed to create GLSL program."); return false; } // Get uniform addresses #define P_GET_UNIFM_LOC(name, target) { \ ppass->target = glGetUniformLocation(ppass->prog, name); \ if (ppass->target < 0) { \ printf_errf("(): Failed to get location of %d-th uniform '" name "'. Might be troublesome.", i); \ } \ } P_GET_UNIFM_LOC("factor_center", unifm_factor_center); if (!ps->o.glx_use_gpushader4) { P_GET_UNIFM_LOC("offset_x", unifm_offset_x); P_GET_UNIFM_LOC("offset_y", unifm_offset_y); } #undef P_GET_UNIFM_LOC } free(extension); // Restore LC_NUMERIC setlocale(LC_NUMERIC, lc_numeric_old); free(lc_numeric_old); } glx_check_err(ps); return true; #else printf_errf("(): GLSL support not compiled in. Cannot do blur with GLX backend."); return false; #endif } #ifdef CONFIG_VSYNC_OPENGL_GLSL /** * Load a GLSL main program from shader strings. */ bool glx_load_prog_main(session_t *ps, const char *vshader_str, const char *fshader_str, glx_prog_main_t *pprogram) { assert(pprogram); // Build program pprogram->prog = glx_create_program_from_str(vshader_str, fshader_str); if (!pprogram->prog) { printf_errf("(): Failed to create GLSL program."); return false; } // Get uniform addresses #define P_GET_UNIFM_LOC(name, target) { \ pprogram->target = glGetUniformLocation(pprogram->prog, name); \ if (pprogram->target < 0) { \ printf_errf("(): Failed to get location of uniform '" name "'. Might be troublesome."); \ } \ } P_GET_UNIFM_LOC("opacity", unifm_opacity); P_GET_UNIFM_LOC("invert_color", unifm_invert_color); P_GET_UNIFM_LOC("tex", unifm_tex); #undef P_GET_UNIFM_LOC glx_check_err(ps); return true; } #endif /** * @brief Update the FBConfig of given depth. */ static inline void glx_update_fbconfig_bydepth(session_t *ps, int depth, glx_fbconfig_t *pfbcfg) { // Make sure the depth is sane if (depth < 0 || depth > OPENGL_MAX_DEPTH) return; // Compare new FBConfig with current one if (glx_cmp_fbconfig(ps, ps->psglx->fbconfigs[depth], pfbcfg) < 0) { #ifdef DEBUG_GLX printf_dbgf("(%d): %#x overrides %#x, target %#x.\n", depth, (unsigned) pfbcfg->cfg, (ps->psglx->fbconfigs[depth] ? (unsigned) ps->psglx->fbconfigs[depth]->cfg: 0), pfbcfg->texture_tgts); #endif if (!ps->psglx->fbconfigs[depth]) { ps->psglx->fbconfigs[depth] = malloc(sizeof(glx_fbconfig_t)); allocchk(ps->psglx->fbconfigs[depth]); } (*ps->psglx->fbconfigs[depth]) = *pfbcfg; } } /** * Get GLX FBConfigs for all depths. */ static bool glx_update_fbconfig(session_t *ps) { // Acquire all FBConfigs and loop through them int nele = 0; GLXFBConfig* pfbcfgs = glXGetFBConfigs(ps->dpy, ps->scr, &nele); for (GLXFBConfig *pcur = pfbcfgs; pcur < pfbcfgs + nele; pcur++) { glx_fbconfig_t fbinfo = { .cfg = *pcur, .texture_fmt = 0, .texture_tgts = 0, .y_inverted = false, }; int id = (int) (pcur - pfbcfgs); int depth = 0, depth_alpha = 0, val = 0; // Skip over multi-sampled visuals // http://people.freedesktop.org/~glisse/0001-glx-do-not-use-multisample-visual-config-for-front-o.patch #ifdef GLX_SAMPLES if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_SAMPLES, &val) && val > 1) continue; #endif if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BUFFER_SIZE, &depth) || Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_ALPHA_SIZE, &depth_alpha)) { printf_errf("(): Failed to retrieve buffer size and alpha size of FBConfig %d.", id); continue; } if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_TARGETS_EXT, &fbinfo.texture_tgts)) { printf_errf("(): Failed to retrieve BIND_TO_TEXTURE_TARGETS_EXT of FBConfig %d.", id); continue; } int visualdepth = 0; { XVisualInfo *pvi = glXGetVisualFromFBConfig(ps->dpy, *pcur); if (!pvi) { // On nvidia-drivers-325.08 this happens slightly too often... // printf_errf("(): Failed to retrieve X Visual of FBConfig %d.", id); continue; } visualdepth = pvi->depth; cxfree(pvi); } bool rgb = false; bool rgba = false; if (depth >= 32 && depth_alpha && Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGBA_EXT, &val) && val) rgba = true; if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGB_EXT, &val) && val) rgb = true; if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_Y_INVERTED_EXT, &val)) fbinfo.y_inverted = val; { int tgtdpt = depth - depth_alpha; if (tgtdpt == visualdepth && tgtdpt < 32 && rgb) { fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGB_EXT; glx_update_fbconfig_bydepth(ps, tgtdpt, &fbinfo); } } if (depth == visualdepth && rgba) { fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGBA_EXT; glx_update_fbconfig_bydepth(ps, depth, &fbinfo); } } cxfree(pfbcfgs); // Sanity checks if (!ps->psglx->fbconfigs[ps->depth]) { printf_errf("(): No FBConfig found for default depth %d.", ps->depth); return false; } if (!ps->psglx->fbconfigs[32]) { printf_errf("(): No FBConfig found for depth 32. Expect crazy things."); } #ifdef DEBUG_GLX printf_dbgf("(): %d-bit: %#3x, 32-bit: %#3x\n", ps->depth, (int) ps->psglx->fbconfigs[ps->depth]->cfg, (int) ps->psglx->fbconfigs[32]->cfg); #endif return true; } static inline int glx_cmp_fbconfig_cmpattr(session_t *ps, const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b, int attr) { int attr_a = 0, attr_b = 0; // TODO: Error checking glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, attr, &attr_a); glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, attr, &attr_b); return attr_a - attr_b; } /** * Compare two GLX FBConfig's to find the preferred one. */ static int glx_cmp_fbconfig(session_t *ps, const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b) { int result = 0; if (!pfbc_a) return -1; if (!pfbc_b) return 1; #define P_CMPATTR_LT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return -result; } #define P_CMPATTR_GT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return result; } P_CMPATTR_LT(GLX_BIND_TO_TEXTURE_RGBA_EXT); P_CMPATTR_LT(GLX_DOUBLEBUFFER); P_CMPATTR_LT(GLX_STENCIL_SIZE); P_CMPATTR_LT(GLX_DEPTH_SIZE); P_CMPATTR_GT(GLX_BIND_TO_MIPMAP_TEXTURE_EXT); return 0; } /** * Bind a X pixmap to an OpenGL texture. */ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, unsigned width, unsigned height, unsigned depth) { if (!pixmap) { printf_errf("(%#010lx): Binding to an empty pixmap. This can't work.", pixmap); return false; } glx_texture_t *ptex = *pptex; bool need_release = true; // Allocate structure if (!ptex) { static const glx_texture_t GLX_TEX_DEF = { .texture = 0, .glpixmap = 0, .pixmap = 0, .target = 0, .width = 0, .height = 0, .depth = 0, .y_inverted = false, }; ptex = malloc(sizeof(glx_texture_t)); allocchk(ptex); memcpy(ptex, &GLX_TEX_DEF, sizeof(glx_texture_t)); *pptex = ptex; } // Release pixmap if parameters are inconsistent if (ptex->texture && ptex->pixmap != pixmap) { glx_release_pixmap(ps, ptex); } // Create GLX pixmap if (!ptex->glpixmap) { need_release = false; // Retrieve pixmap parameters, if they aren't provided if (!(width && height && depth)) { Window rroot = None; int rx = 0, ry = 0; unsigned rbdwid = 0; if (!XGetGeometry(ps->dpy, pixmap, &rroot, &rx, &ry, &width, &height, &rbdwid, &depth)) { printf_errf("(%#010lx): Failed to query Pixmap info.", pixmap); return false; } if (depth > OPENGL_MAX_DEPTH) { printf_errf("(%d): Requested depth higher than %d.", depth, OPENGL_MAX_DEPTH); return false; } } const glx_fbconfig_t *pcfg = ps->psglx->fbconfigs[depth]; if (!pcfg) { printf_errf("(%d): Couldn't find FBConfig with requested depth.", depth); return false; } // Determine texture target, copied from compiz // The assumption we made here is the target never changes based on any // pixmap-specific parameters, and this may change in the future GLenum tex_tgt = 0; if (GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts && ps->psglx->has_texture_non_power_of_two) tex_tgt = GLX_TEXTURE_2D_EXT; else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & pcfg->texture_tgts) tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; else if (!(GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts)) tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; else tex_tgt = GLX_TEXTURE_2D_EXT; #ifdef DEBUG_GLX printf_dbgf("(): depth %d, tgt %#x, rgba %d\n", depth, tex_tgt, (GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt)); #endif GLint attrs[] = { GLX_TEXTURE_FORMAT_EXT, pcfg->texture_fmt, GLX_TEXTURE_TARGET_EXT, tex_tgt, 0, }; ptex->glpixmap = glXCreatePixmap(ps->dpy, pcfg->cfg, pixmap, attrs); ptex->pixmap = pixmap; ptex->target = (GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D: GL_TEXTURE_RECTANGLE); ptex->width = width; ptex->height = height; ptex->depth = depth; ptex->y_inverted = pcfg->y_inverted; } if (!ptex->glpixmap) { printf_errf("(): Failed to allocate GLX pixmap."); return false; } glEnable(ptex->target); // Create texture if (!ptex->texture) { need_release = false; GLuint texture = 0; glGenTextures(1, &texture); glBindTexture(ptex->target, texture); glTexParameteri(ptex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(ptex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(ptex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(ptex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(ptex->target, 0); ptex->texture = texture; } if (!ptex->texture) { printf_errf("(): Failed to allocate texture."); return false; } glBindTexture(ptex->target, ptex->texture); // The specification requires rebinding whenever the content changes... // We can't follow this, too slow. if (need_release) ps->psglx->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); ps->psglx->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL); // Cleanup glBindTexture(ptex->target, 0); glDisable(ptex->target); glx_check_err(ps); return true; } /** * @brief Release binding of a texture. */ void glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { // Release binding if (ptex->glpixmap && ptex->texture) { glBindTexture(ptex->target, ptex->texture); ps->psglx->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); glBindTexture(ptex->target, 0); } // Free GLX Pixmap if (ptex->glpixmap) { glXDestroyPixmap(ps->dpy, ptex->glpixmap); ptex->glpixmap = 0; } glx_check_err(ps); } /** * Preprocess function before start painting. */ void glx_paint_pre(session_t *ps, XserverRegion *preg) { ps->psglx->z = 0.0; // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Get buffer age bool trace_damage = (ps->o.glx_swap_method < 0 || ps->o.glx_swap_method > 1); // Trace raw damage regions XserverRegion newdamage = None; if (trace_damage && *preg) newdamage = copy_region(ps, *preg); // OpenGL doesn't support partial repaint without GLX_MESA_copy_sub_buffer, // we could redraw the whole screen or copy unmodified pixels from // front buffer with --glx-copy-from-front. if (ps->o.glx_use_copysubbuffermesa || !*preg) { } else { int buffer_age = ps->o.glx_swap_method; // Getting buffer age { // Query GLX_EXT_buffer_age for buffer age if (SWAPM_BUFFER_AGE == buffer_age) { unsigned val = 0; glXQueryDrawable(ps->dpy, get_tgt_window(ps), GLX_BACK_BUFFER_AGE_EXT, &val); buffer_age = val; } // Buffer age too high if (buffer_age > CGLX_MAX_BUFFER_AGE + 1) buffer_age = 0; // Make sure buffer age >= 0 buffer_age = max_i(buffer_age, 0); // Check if we have we have empty regions if (buffer_age > 1) { for (int i = 0; i < buffer_age - 1; ++i) if (!ps->all_damage_last[i]) { buffer_age = 0; break; } } } // Do nothing for buffer_age 1 (copy) if (1 != buffer_age) { // Copy pixels if (ps->o.glx_copy_from_front) { // Determine copy area XserverRegion reg_copy = XFixesCreateRegion(ps->dpy, NULL, 0); if (!buffer_age) { XFixesSubtractRegion(ps->dpy, reg_copy, ps->screen_reg, *preg); } else { for (int i = 0; i < buffer_age - 1; ++i) XFixesUnionRegion(ps->dpy, reg_copy, reg_copy, ps->all_damage_last[i]); XFixesSubtractRegion(ps->dpy, reg_copy, reg_copy, *preg); } // Actually copy pixels { GLfloat raster_pos[4]; GLfloat curx = 0.0f, cury = 0.0f; glGetFloatv(GL_CURRENT_RASTER_POSITION, raster_pos); glReadBuffer(GL_FRONT); glRasterPos2f(0.0, 0.0); { int nrects = 0; XRectangle *rects = XFixesFetchRegion(ps->dpy, reg_copy, &nrects); for (int i = 0; i < nrects; ++i) { const int x = rects[i].x; const int y = ps->root_height - rects[i].y - rects[i].height; // Kwin patch says glRasterPos2f() causes artifacts on bottom // screen edge with some drivers glBitmap(0, 0, 0, 0, x - curx, y - cury, NULL); curx = x; cury = y; glCopyPixels(x, y, rects[i].width, rects[i].height, GL_COLOR); } cxfree(rects); } glReadBuffer(GL_BACK); glRasterPos4fv(raster_pos); } free_region(ps, ®_copy); } // Determine paint area if (ps->o.glx_copy_from_front) { } else if (buffer_age) { for (int i = 0; i < buffer_age - 1; ++i) XFixesUnionRegion(ps->dpy, *preg, *preg, ps->all_damage_last[i]); } else { free_region(ps, preg); } } } if (trace_damage) { free_region(ps, &ps->all_damage_last[CGLX_MAX_BUFFER_AGE - 1]); memmove(ps->all_damage_last + 1, ps->all_damage_last, (CGLX_MAX_BUFFER_AGE - 1) * sizeof(XserverRegion)); ps->all_damage_last[0] = newdamage; } glx_set_clip(ps, *preg, NULL); #ifdef DEBUG_GLX_PAINTREG glx_render_color(ps, 0, 0, ps->root_width, ps->root_height, 0, *preg, NULL); #endif glx_check_err(ps); } /** * Set clipping region on the target window. */ void glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { // Quit if we aren't using stencils if (ps->o.glx_no_stencil) return; static XRectangle rect_blank = { .x = 0, .y = 0, .width = 0, .height = 0 }; glDisable(GL_STENCIL_TEST); glDisable(GL_SCISSOR_TEST); if (!reg) return; int nrects = 0; XRectangle *rects_free = NULL; const XRectangle *rects = NULL; if (pcache_reg) { rects = pcache_reg->rects; nrects = pcache_reg->nrects; } if (!rects) { nrects = 0; rects = rects_free = XFixesFetchRegion(ps->dpy, reg, &nrects); } // Use one empty rectangle if the region is empty if (!nrects) { cxfree(rects_free); rects_free = NULL; nrects = 1; rects = &rect_blank; } assert(nrects); if (1 == nrects) { glEnable(GL_SCISSOR_TEST); glScissor(rects[0].x, ps->root_height - rects[0].y - rects[0].height, rects[0].width, rects[0].height); } else { glEnable(GL_STENCIL_TEST); glClear(GL_STENCIL_BUFFER_BIT); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_FALSE); glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); glBegin(GL_QUADS); for (int i = 0; i < nrects; ++i) { GLint rx = rects[i].x; GLint ry = ps->root_height - rects[i].y; GLint rxe = rx + rects[i].width; GLint rye = ry - rects[i].height; GLint z = 0; #ifdef DEBUG_GLX printf_dbgf("(): Rect %d: %d, %d, %d, %d\n", i, rx, ry, rxe, rye); #endif glVertex3i(rx, ry, z); glVertex3i(rxe, ry, z); glVertex3i(rxe, rye, z); glVertex3i(rx, rye, z); } glEnd(); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // glDepthMask(GL_TRUE); } cxfree(rects_free); glx_check_err(ps); } #define P_PAINTREG_START() \ XserverRegion reg_new = None; \ XRectangle rec_all = { .x = dx, .y = dy, .width = width, .height = height }; \ XRectangle *rects = &rec_all; \ int nrects = 1; \ \ if (ps->o.glx_no_stencil && reg_tgt) { \ if (pcache_reg) { \ rects = pcache_reg->rects; \ nrects = pcache_reg->nrects; \ } \ else { \ reg_new = XFixesCreateRegion(ps->dpy, &rec_all, 1); \ XFixesIntersectRegion(ps->dpy, reg_new, reg_new, reg_tgt); \ \ nrects = 0; \ rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects); \ } \ } \ glBegin(GL_QUADS); \ \ for (int ri = 0; ri < nrects; ++ri) { \ XRectangle crect; \ rect_crop(&crect, &rects[ri], &rec_all); \ \ if (!crect.width || !crect.height) \ continue; \ #define P_PAINTREG_END() \ } \ glEnd(); \ \ if (rects && rects != &rec_all && !(pcache_reg && pcache_reg->rects == rects)) \ cxfree(rects); \ free_region(ps, ®_new); \ static inline GLuint glx_gen_texture(session_t *ps, GLenum tex_tgt, int width, int height) { GLuint tex = 0; glGenTextures(1, &tex); if (!tex) return 0; glEnable(tex_tgt); glBindTexture(tex_tgt, tex); glTexParameteri(tex_tgt, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(tex_tgt, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(tex_tgt, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glBindTexture(tex_tgt, 0); return tex; } static inline void glx_copy_region_to_tex(session_t *ps, GLenum tex_tgt, int basex, int basey, int dx, int dy, int width, int height) { if (width > 0 && height > 0) glCopyTexSubImage2D(tex_tgt, 0, dx - basex, dy - basey, dx, ps->root_height - dy - height, width, height); } #ifdef CONFIG_VSYNC_OPENGL_GLSL /** * Blur contents in a particular region. */ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, GLfloat factor_center, XserverRegion reg_tgt, const reg_data_t *pcache_reg, glx_blur_cache_t *pbc) { assert(ps->psglx->blur_passes[0].prog); const bool more_passes = ps->psglx->blur_passes[1].prog; const bool have_scissors = glIsEnabled(GL_SCISSOR_TEST); const bool have_stencil = glIsEnabled(GL_STENCIL_TEST); bool ret = false; // Calculate copy region size glx_blur_cache_t ibc = { .width = 0, .height = 0 }; if (!pbc) pbc = &ibc; int mdx = dx, mdy = dy, mwidth = width, mheight = height; #ifdef DEBUG_GLX printf_dbgf("(): %d, %d, %d, %d\n", mdx, mdy, mwidth, mheight); #endif /* if (ps->o.resize_damage > 0) { int inc_x = 0, inc_y = 0; for (int i = 0; i < MAX_BLUR_PASS; ++i) { XFixed *kern = ps->o.blur_kerns[i]; if (!kern) break; inc_x += XFixedToDouble(kern[0]) / 2; inc_y += XFixedToDouble(kern[1]) / 2; } inc_x = min_i(ps->o.resize_damage, inc_x); inc_y = min_i(ps->o.resize_damage, inc_y); mdx = max_i(dx - inc_x, 0); mdy = max_i(dy - inc_y, 0); int mdx2 = min_i(dx + width + inc_x, ps->root_width), mdy2 = min_i(dy + height + inc_y, ps->root_height); mwidth = mdx2 - mdx; mheight = mdy2 - mdy; } */ GLenum tex_tgt = GL_TEXTURE_RECTANGLE; if (ps->psglx->has_texture_non_power_of_two) tex_tgt = GL_TEXTURE_2D; // Free textures if size inconsistency discovered if (mwidth != pbc->width || mheight != pbc->height) free_glx_bc_resize(ps, pbc); // Generate FBO and textures if needed if (!pbc->textures[0]) pbc->textures[0] = glx_gen_texture(ps, tex_tgt, mwidth, mheight); GLuint tex_scr = pbc->textures[0]; if (more_passes && !pbc->textures[1]) pbc->textures[1] = glx_gen_texture(ps, tex_tgt, mwidth, mheight); pbc->width = mwidth; pbc->height = mheight; GLuint tex_scr2 = pbc->textures[1]; #ifdef CONFIG_VSYNC_OPENGL_FBO if (more_passes && !pbc->fbo) glGenFramebuffers(1, &pbc->fbo); const GLuint fbo = pbc->fbo; #endif if (!tex_scr || (more_passes && !tex_scr2)) { printf_errf("(): Failed to allocate texture."); goto glx_blur_dst_end; } #ifdef CONFIG_VSYNC_OPENGL_FBO if (more_passes && !fbo) { printf_errf("(): Failed to allocate framebuffer."); goto glx_blur_dst_end; } #endif // Read destination pixels into a texture glEnable(tex_tgt); glBindTexture(tex_tgt, tex_scr); glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, mdy, mwidth, mheight); /* if (tex_scr2) { glBindTexture(tex_tgt, tex_scr2); glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, mdy, mwidth, dx - mdx); glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, dy + height, mwidth, mdy + mheight - dy - height); glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, dy, dx - mdx, height); glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, dx + width, dy, mdx + mwidth - dx - width, height); } */ // Texture scaling factor GLfloat texfac_x = 1.0f, texfac_y = 1.0f; if (GL_TEXTURE_2D == tex_tgt) { texfac_x /= mwidth; texfac_y /= mheight; } // Paint it back if (more_passes) { glDisable(GL_STENCIL_TEST); glDisable(GL_SCISSOR_TEST); } bool last_pass = false; for (int i = 0; !last_pass; ++i) { last_pass = !ps->psglx->blur_passes[i + 1].prog; assert(i < MAX_BLUR_PASS - 1); const glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; assert(ppass->prog); assert(tex_scr); glBindTexture(tex_tgt, tex_scr); #ifdef CONFIG_VSYNC_OPENGL_FBO if (!last_pass) { static const GLenum DRAWBUFS[2] = { GL_COLOR_ATTACHMENT0 }; glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_scr2, 0); glDrawBuffers(1, DRAWBUFS); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { printf_errf("(): Framebuffer attachment failed."); goto glx_blur_dst_end; } } else { static const GLenum DRAWBUFS[2] = { GL_BACK }; glBindFramebuffer(GL_FRAMEBUFFER, 0); glDrawBuffers(1, DRAWBUFS); if (have_scissors) glEnable(GL_SCISSOR_TEST); if (have_stencil) glEnable(GL_STENCIL_TEST); } #endif // Color negation for testing... // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); // glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); // glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glUseProgram(ppass->prog); if (ppass->unifm_offset_x >= 0) glUniform1f(ppass->unifm_offset_x, texfac_x); if (ppass->unifm_offset_y >= 0) glUniform1f(ppass->unifm_offset_y, texfac_y); if (ppass->unifm_factor_center >= 0) glUniform1f(ppass->unifm_factor_center, factor_center); { P_PAINTREG_START(); { const GLfloat rx = (crect.x - mdx) * texfac_x; const GLfloat ry = (mheight - (crect.y - mdy)) * texfac_y; const GLfloat rxe = rx + crect.width * texfac_x; const GLfloat rye = ry - crect.height * texfac_y; GLfloat rdx = crect.x - mdx; GLfloat rdy = mheight - crect.y + mdy; GLfloat rdxe = rdx + crect.width; GLfloat rdye = rdy - crect.height; if (last_pass) { rdx = crect.x; rdy = ps->root_height - crect.y; rdxe = rdx + crect.width; rdye = rdy - crect.height; } #ifdef DEBUG_GLX printf_dbgf("(): %f, %f, %f, %f -> %f, %f, %f, %f\n", rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); #endif glTexCoord2f(rx, ry); glVertex3f(rdx, rdy, z); glTexCoord2f(rxe, ry); glVertex3f(rdxe, rdy, z); glTexCoord2f(rxe, rye); glVertex3f(rdxe, rdye, z); glTexCoord2f(rx, rye); glVertex3f(rdx, rdye, z); } P_PAINTREG_END(); } glUseProgram(0); // Swap tex_scr and tex_scr2 { GLuint tmp = tex_scr2; tex_scr2 = tex_scr; tex_scr = tmp; } } ret = true; glx_blur_dst_end: #ifdef CONFIG_VSYNC_OPENGL_FBO glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif glBindTexture(tex_tgt, 0); glDisable(tex_tgt); if (have_scissors) glEnable(GL_SCISSOR_TEST); if (have_stencil) glEnable(GL_STENCIL_TEST); if (&ibc == pbc) { free_glx_bc(ps, pbc); } glx_check_err(ps); return ret; } #endif bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg) { // It's possible to dim in glx_render(), but it would be over-complicated // considering all those mess in color negation and modulation glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glColor4f(0.0f, 0.0f, 0.0f, factor); { P_PAINTREG_START(); { GLint rdx = crect.x; GLint rdy = ps->root_height - crect.y; GLint rdxe = rdx + crect.width; GLint rdye = rdy - crect.height; glVertex3i(rdx, rdy, z); glVertex3i(rdxe, rdy, z); glVertex3i(rdxe, rdye, z); glVertex3i(rdx, rdye, z); } P_PAINTREG_END(); } glEnd(); glColor4f(0.0f, 0.0f, 0.0f, 0.0f); glDisable(GL_BLEND); glx_check_err(ps); return true; } /** * @brief Render a region with texture data. */ bool glx_render_(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, double opacity, bool argb, bool neg, XserverRegion reg_tgt, const reg_data_t *pcache_reg #ifdef CONFIG_VSYNC_OPENGL_GLSL , const glx_prog_main_t *pprogram #endif ) { if (!ptex || !ptex->texture) { printf_errf("(): Missing texture."); return false; } #ifdef DEBUG_GLX_PAINTREG glx_render_dots(ps, dx, dy, width, height, z, reg_tgt, pcache_reg); return true; #endif argb = argb || (GLX_TEXTURE_FORMAT_RGBA_EXT == ps->psglx->fbconfigs[ptex->depth]->texture_fmt); #ifdef CONFIG_VSYNC_OPENGL_GLSL const bool has_prog = pprogram && pprogram->prog; #endif bool dual_texture = false; // It's required by legacy versions of OpenGL to enable texture target // before specifying environment. Thanks to madsy for telling me. glEnable(ptex->target); // Enable blending if needed if (opacity < 1.0 || argb) { glEnable(GL_BLEND); // Needed for handling opacity of ARGB texture glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // This is all weird, but X Render is using premultiplied ARGB format, and // we need to use those things to correct it. Thanks to derhass for help. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glColor4f(opacity, opacity, opacity, opacity); } #ifdef CONFIG_VSYNC_OPENGL_GLSL if (!has_prog) #endif { // The default, fixed-function path // Color negation if (neg) { // Simple color negation if (!glIsEnabled(GL_BLEND)) { glEnable(GL_COLOR_LOGIC_OP); glLogicOp(GL_COPY_INVERTED); } // ARGB texture color negation else if (argb) { dual_texture = true; // Use two texture stages because the calculation is too complicated, // thanks to madsy for providing code // Texture stage 0 glActiveTexture(GL_TEXTURE0); // Negation for premultiplied color: color = A - C glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); // Pass texture alpha through glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); // Texture stage 1 glActiveTexture(GL_TEXTURE1); glEnable(ptex->target); glBindTexture(ptex->target, ptex->texture); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); // Modulation with constant factor glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); // Modulation with constant factor glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); glActiveTexture(GL_TEXTURE0); } // RGB blend color negation else { glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); // Modulation with constant factor glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); // Modulation with constant factor glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); } } } #ifdef CONFIG_VSYNC_OPENGL_GLSL else { // Programmable path assert(pprogram->prog); glUseProgram(pprogram->prog); if (pprogram->unifm_opacity >= 0) glUniform1f(pprogram->unifm_opacity, opacity); if (pprogram->unifm_invert_color >= 0) glUniform1i(pprogram->unifm_invert_color, neg); if (pprogram->unifm_tex >= 0) glUniform1i(pprogram->unifm_tex, 0); } #endif #ifdef DEBUG_GLX printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n", x, y, width, height, dx, dy, ptex->width, ptex->height, z); #endif // Bind texture glBindTexture(ptex->target, ptex->texture); if (dual_texture) { glActiveTexture(GL_TEXTURE1); glBindTexture(ptex->target, ptex->texture); glActiveTexture(GL_TEXTURE0); } // Painting { P_PAINTREG_START(); { GLfloat rx = (double) (crect.x - dx + x); GLfloat ry = (double) (crect.y - dy + y); GLfloat rxe = rx + (double) crect.width; GLfloat rye = ry + (double) crect.height; // Rectangle textures have [0-w] [0-h] while 2D texture has [0-1] [0-1] // Thanks to amonakov for pointing out! if (GL_TEXTURE_2D == ptex->target) { rx = rx / ptex->width; ry = ry / ptex->height; rxe = rxe / ptex->width; rye = rye / ptex->height; } GLint rdx = crect.x; GLint rdy = ps->root_height - crect.y; GLint rdxe = rdx + crect.width; GLint rdye = rdy - crect.height; // Invert Y if needed, this may not work as expected, though. I don't // have such a FBConfig to test with. if (!ptex->y_inverted) { ry = 1.0 - ry; rye = 1.0 - rye; } #ifdef DEBUG_GLX printf_dbgf("(): Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d\n", ri, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); #endif #define P_TEXCOORD(cx, cy) { \ if (dual_texture) { \ glMultiTexCoord2f(GL_TEXTURE0, cx, cy); \ glMultiTexCoord2f(GL_TEXTURE1, cx, cy); \ } \ else glTexCoord2f(cx, cy); \ } P_TEXCOORD(rx, ry); glVertex3i(rdx, rdy, z); P_TEXCOORD(rxe, ry); glVertex3i(rdxe, rdy, z); P_TEXCOORD(rxe, rye); glVertex3i(rdxe, rdye, z); P_TEXCOORD(rx, rye); glVertex3i(rdx, rdye, z); } P_PAINTREG_END(); } // Cleanup glBindTexture(ptex->target, 0); glColor4f(0.0f, 0.0f, 0.0f, 0.0f); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_BLEND); glDisable(GL_COLOR_LOGIC_OP); glDisable(ptex->target); if (dual_texture) { glActiveTexture(GL_TEXTURE1); glBindTexture(ptex->target, 0); glDisable(ptex->target); glActiveTexture(GL_TEXTURE0); } #ifdef CONFIG_VSYNC_OPENGL_GLSL if (has_prog) glUseProgram(0); #endif glx_check_err(ps); return true; } /** * Render a region with color. */ static void glx_render_color(session_t *ps, int dx, int dy, int width, int height, int z, XserverRegion reg_tgt, const reg_data_t *pcache_reg) { static int color = 0; color = color % (3 * 3 * 3 - 1) + 1; glColor4f(1.0 / 3.0 * (color / (3 * 3)), 1.0 / 3.0 * (color % (3 * 3) / 3), 1.0 / 3.0 * (color % 3), 1.0f ); z -= 0.2; { P_PAINTREG_START(); { GLint rdx = crect.x; GLint rdy = ps->root_height - crect.y; GLint rdxe = rdx + crect.width; GLint rdye = rdy - crect.height; glVertex3i(rdx, rdy, z); glVertex3i(rdxe, rdy, z); glVertex3i(rdxe, rdye, z); glVertex3i(rdx, rdye, z); } P_PAINTREG_END(); } glColor4f(0.0f, 0.0f, 0.0f, 0.0f); glx_check_err(ps); } /** * Render a region with dots. */ static void glx_render_dots(session_t *ps, int dx, int dy, int width, int height, int z, XserverRegion reg_tgt, const reg_data_t *pcache_reg) { glColor4f(0.0f, 0.0f, 0.0f, 1.0f); z -= 0.1; { P_PAINTREG_START(); { static const GLint BLK_WID = 5, BLK_HEI = 5; glEnd(); glPointSize(1.0); glBegin(GL_POINTS); GLint rdx = crect.x; GLint rdy = ps->root_height - crect.y; GLint rdxe = rdx + crect.width; GLint rdye = rdy - crect.height; rdx = (rdx) / BLK_WID * BLK_WID; rdy = (rdy) / BLK_HEI * BLK_HEI; rdxe = (rdxe) / BLK_WID * BLK_WID; rdye = (rdye) / BLK_HEI * BLK_HEI; for (GLint cdx = rdx; cdx < rdxe; cdx += BLK_WID) for (GLint cdy = rdy; cdy > rdye; cdy -= BLK_HEI) glVertex3i(cdx + BLK_WID / 2, cdy - BLK_HEI / 2, z); } P_PAINTREG_END(); } glColor4f(0.0f, 0.0f, 0.0f, 0.0f); glx_check_err(ps); } /** * Swap buffer with glXCopySubBufferMESA(). */ void glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { int nrects = 0; XRectangle *rects = XFixesFetchRegion(ps->dpy, reg, &nrects); if (1 == nrects && rect_is_fullscreen(ps, rects[0].x, rects[0].y, rects[0].width, rects[0].height)) { glXSwapBuffers(ps->dpy, get_tgt_window(ps)); } else { glx_set_clip(ps, None, NULL); for (int i = 0; i < nrects; ++i) { const int x = rects[i].x; const int y = ps->root_height - rects[i].y - rects[i].height; const int wid = rects[i].width; const int hei = rects[i].height; #ifdef DEBUG_GLX printf_dbgf("(): %d, %d, %d, %d\n", x, y, wid, hei); #endif ps->psglx->glXCopySubBufferProc(ps->dpy, get_tgt_window(ps), x, y, wid, hei); } } glx_check_err(ps); cxfree(rects); } /** * @brief Get tightly packed RGB888 data from GL front buffer. * * Don't expect any sort of decent performance. * * @returns tightly packed RGB888 data of the size of the screen, * to be freed with `free()` */ unsigned char * glx_take_screenshot(session_t *ps, int *out_length) { int length = 3 * ps->root_width * ps->root_height; GLint unpack_align_old = 0; glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_align_old); assert(unpack_align_old > 0); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); unsigned char *buf = cmalloc(length, unsigned char); glReadBuffer(GL_FRONT); glReadPixels(0, 0, ps->root_width, ps->root_height, GL_RGB, GL_UNSIGNED_BYTE, buf); glReadBuffer(GL_BACK); glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_align_old); if (out_length) *out_length = sizeof(unsigned char) * length; return buf; } #ifdef CONFIG_VSYNC_OPENGL_GLSL GLuint glx_create_shader(GLenum shader_type, const char *shader_str) { #ifdef DEBUG_GLX_GLSL printf("glx_create_shader(): ===\n%s\n===\n", shader_str); fflush(stdout); #endif bool success = false; GLuint shader = glCreateShader(shader_type); if (!shader) { printf_errf("(): Failed to create shader with type %#x.", shader_type); goto glx_create_shader_end; } glShaderSource(shader, 1, &shader_str, NULL); glCompileShader(shader); // Get shader status { GLint status = GL_FALSE; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (GL_FALSE == status) { GLint log_len = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len); if (log_len) { char log[log_len + 1]; glGetShaderInfoLog(shader, log_len, NULL, log); printf_errf("(): Failed to compile shader with type %d: %s", shader_type, log); } goto glx_create_shader_end; } } success = true; glx_create_shader_end: if (shader && !success) { glDeleteShader(shader); shader = 0; } return shader; } GLuint glx_create_program(const GLuint * const shaders, int nshaders) { bool success = false; GLuint program = glCreateProgram(); if (!program) { printf_errf("(): Failed to create program."); goto glx_create_program_end; } for (int i = 0; i < nshaders; ++i) glAttachShader(program, shaders[i]); glLinkProgram(program); // Get program status { GLint status = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &status); if (GL_FALSE == status) { GLint log_len = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len); if (log_len) { char log[log_len + 1]; glGetProgramInfoLog(program, log_len, NULL, log); printf_errf("(): Failed to link program: %s", log); } goto glx_create_program_end; } } success = true; glx_create_program_end: if (program) { for (int i = 0; i < nshaders; ++i) glDetachShader(program, shaders[i]); } if (program && !success) { glDeleteProgram(program); program = 0; } return program; } /** * @brief Create a program from vertex and fragment shader strings. */ GLuint glx_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str) { GLuint vert_shader = 0; GLuint frag_shader = 0; GLuint prog = 0; if (vert_shader_str) vert_shader = glx_create_shader(GL_VERTEX_SHADER, vert_shader_str); if (frag_shader_str) frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, frag_shader_str); { GLuint shaders[2]; int count = 0; if (vert_shader) shaders[count++] = vert_shader; if (frag_shader) shaders[count++] = frag_shader; assert(count <= sizeof(shaders) / sizeof(shaders[0])); if (count) prog = glx_create_program(shaders, count); } if (vert_shader) glDeleteShader(vert_shader); if (frag_shader) glDeleteShader(frag_shader); return prog; } #endif compton-0.1~beta2+20150922/src/opengl.h000066400000000000000000000064541260012102400172660ustar00rootroot00000000000000/* * Compton - a compositor for X11 * * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * * Copyright (c) 2011-2013, Christopher Jeffrey * See LICENSE for more information. * */ #include "common.h" #include #include #ifdef DEBUG_GLX_ERR /** * Get a textual representation of an OpenGL error. */ static inline const char * glx_dump_err_str(GLenum err) { switch (err) { CASESTRRET(GL_NO_ERROR); CASESTRRET(GL_INVALID_ENUM); CASESTRRET(GL_INVALID_VALUE); CASESTRRET(GL_INVALID_OPERATION); CASESTRRET(GL_INVALID_FRAMEBUFFER_OPERATION); CASESTRRET(GL_OUT_OF_MEMORY); CASESTRRET(GL_STACK_UNDERFLOW); CASESTRRET(GL_STACK_OVERFLOW); } return NULL; } /** * Check for GLX error. * * http://blog.nobel-joergensen.com/2013/01/29/debugging-opengl-using-glgeterror/ */ static inline void glx_check_err_(session_t *ps, const char *func, int line) { if (!ps->psglx->context) return; GLenum err = GL_NO_ERROR; while (GL_NO_ERROR != (err = glGetError())) { print_timestamp(ps); printf("%s():%d: GLX error ", func, line); const char *errtext = glx_dump_err_str(err); if (errtext) { printf_dbg("%s\n", errtext); } else { printf_dbg("%d\n", err); } } } #define glx_check_err(ps) glx_check_err_(ps, __func__, __LINE__) #else #define glx_check_err(ps) ((void) 0) #endif /** * Check if a word is in string. */ static inline bool wd_is_in_str(const char *haystick, const char *needle) { if (!haystick) return false; assert(*needle); const char *pos = haystick - 1; while ((pos = strstr(pos + 1, needle))) { // Continue if it isn't a word boundary if (((pos - haystick) && !isspace(*(pos - 1))) || (strlen(pos) > strlen(needle) && !isspace(pos[strlen(needle)]))) continue; return true; } return false; } /** * Check if a GLX extension exists. */ static inline bool glx_hasglxext(session_t *ps, const char *ext) { const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr); if (!glx_exts) { printf_errf("(): Failed get GLX extension list."); return false; } bool found = wd_is_in_str(glx_exts, ext); if (!found) printf_errf("(): Missing GLX extension %s.", ext); return found; } /** * Check if a GLX extension exists. */ static inline bool glx_hasglext(session_t *ps, const char *ext) { const char *gl_exts = (const char *) glGetString(GL_EXTENSIONS); if (!gl_exts) { printf_errf("(): Failed get GL extension list."); return false; } bool found = wd_is_in_str(gl_exts, ext); if (!found) printf_errf("(): Missing GL extension %s.", ext); return found; } static inline XVisualInfo * get_visualinfo_from_visual(session_t *ps, Visual *visual) { XVisualInfo vreq = { .visualid = XVisualIDFromVisual(visual) }; int nitems = 0; return XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems); } static bool glx_update_fbconfig(session_t *ps); static int glx_cmp_fbconfig(session_t *ps, const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b); static void glx_render_color(session_t *ps, int dx, int dy, int width, int height, int z, XserverRegion reg_tgt, const reg_data_t *pcache_reg); static void glx_render_dots(session_t *ps, int dx, int dy, int width, int height, int z, XserverRegion reg_tgt, const reg_data_t *pcache_reg); compton-0.1~beta2+20150922/src/xrescheck.c000066400000000000000000000030721260012102400177450ustar00rootroot00000000000000#include "xrescheck.h" static xrc_xid_record_t *gs_xid_records = NULL; #define HASH_ADD_XID(head, xidfield, add) \ HASH_ADD(hh, head, xidfield, sizeof(xid), add) #define HASH_FIND_XID(head, findxid, out) \ HASH_FIND(hh, head, findxid, sizeof(xid), out) #define M_CPY_POS_DATA(prec) \ prec->file = file; \ prec->func = func; \ prec->line = line; \ /** * @brief Add a record of given XID to the allocation table. */ void xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS) { xrc_xid_record_t *prec = cmalloc(1, xrc_xid_record_t); prec->xid = xid; prec->type = type; M_CPY_POS_DATA(prec); HASH_ADD_XID(gs_xid_records, xid, prec); } /** * @brief Delete a record of given XID in the allocation table. */ void xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS) { xrc_xid_record_t *prec = NULL; HASH_FIND_XID(gs_xid_records, &xid, prec); if (!prec) { printf_err("XRC: %s:%d %s(): Can't find XID %#010lx we want to delete.", file, line, func, xid); return; } HASH_DEL(gs_xid_records, prec); free(prec); } /** * @brief Report about issues found in the XID allocation table. */ void xrc_report_xid(void) { for (xrc_xid_record_t *prec = gs_xid_records; prec; prec = prec->hh.next) printf_dbg("XRC: %s:%d %s(): %#010lx (%s) not freed.\n", prec->file, prec->line, prec->func, prec->xid, prec->type); } /** * @brief Clear the XID allocation table. */ void xrc_clear_xid(void) { xrc_xid_record_t *prec = NULL, *ptmp = NULL; HASH_ITER(hh, gs_xid_records, prec, ptmp) { HASH_DEL(gs_xid_records, prec); free(prec); } } compton-0.1~beta2+20150922/src/xrescheck.h000066400000000000000000000033271260012102400177550ustar00rootroot00000000000000#ifndef COMPTON_XRESCHECK_H #define COMPTON_XRESCHECK_H #include "common.h" #include typedef struct { XID xid; const char *type; const char *file; const char *func; int line; UT_hash_handle hh; } xrc_xid_record_t; #define M_POS_DATA_PARAMS const char *file, int line, const char *func #define M_POS_DATA_PASSTHROUGH file, line, func #define M_POS_DATA __FILE__, __LINE__, __func__ void xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS); #define xrc_add_xid(xid, type) xrc_add_xid_(xid, type, M_POS_DATA) void xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS); #define xrc_delete_xid(xid) xrc_delete_xid_(xid, M_POS_DATA) void xrc_report_xid(void); void xrc_clear_xid(void); // Pixmap static inline Pixmap XCreatePixmap_(Display *dpy, Drawable drawable, unsigned int width, unsigned int height, unsigned int depth, M_POS_DATA_PARAMS) { Pixmap ret = XCreatePixmap(dpy, drawable, width, height, depth); if (ret) xrc_add_xid_(ret, "Pixmap", M_POS_DATA_PASSTHROUGH); return ret; } #define XCreatePixmap(dpy, drawable, width, height, depth) \ XCreatePixmap_(dpy, drawable, width, height, depth, M_POS_DATA) static inline Pixmap XCompositeNameWindowPixmap_(Display *dpy, Window window, M_POS_DATA_PARAMS) { Pixmap ret = XCompositeNameWindowPixmap(dpy, window); if (ret) xrc_add_xid_(ret, "PixmapC", M_POS_DATA_PASSTHROUGH); return ret; } #define XCompositeNameWindowPixmap(dpy, window) \ XCompositeNameWindowPixmap_(dpy, window, M_POS_DATA) static inline void XFreePixmap_(Display *dpy, Pixmap pixmap, M_POS_DATA_PARAMS) { XFreePixmap(dpy, pixmap); xrc_delete_xid_(pixmap, M_POS_DATA_PASSTHROUGH); } #define XFreePixmap(dpy, pixmap) XFreePixmap_(dpy, pixmap, M_POS_DATA); #endif compton-0.1~beta2+20150922/tests/000077500000000000000000000000001260012102400161735ustar00rootroot00000000000000compton-0.1~beta2+20150922/tests/cmake-test.sh000077500000000000000000000011261260012102400205670ustar00rootroot00000000000000#!/bin/bash # Test script for CMake build BASE_DIR=$(dirname "$0")/.. . "${BASE_DIR}/functions.sh" BUILD_DIR="build" cmake_prepare() { [ ! -e "CMakeLists.txt" ] && ln -s {_,}CMakeLists.txt } cmake_build() { einfo Building compton with cmake $@ [ -e "${BUILD_DIR}" ] && rm -r "${BUILD_DIR}" mkdir "${BUILD_DIR}" && cd "${BUILD_DIR}" || die cmake ${@} .. || die make VERBOSE=1 -B || die cd - einfo Build completed successfully } show_build_help_msg() { "${BUILD_DIR}/compton" -h | less } main() { cmake_prepare cmake_build "${@}" # show_build_help_msg } main "${@}" compton-0.1~beta2+20150922/tests/make-tests.sh000077500000000000000000000006311260012102400206070ustar00rootroot00000000000000#!/bin/bash # Test script for GNU make build BASE_DIR=$(dirname "$0")/.. . "${BASE_DIR}/functions.sh" OPTIONS=( NO_XINERAMA NO_LIBCONFIG NO_REGEX_PCRE NO_REGEX_PCRE_JIT NO_VSYNC_DRM NO_VSYNC_OPENGL NO_VSYNC_OPENGL_GLSL NO_VSYNC_OPENGL_FBO NO_VSYNC_OPENGL_VBO NO_DBUS NO_XSYNC NO_C2 ) for o in "${OPTIONS[@]}"; do einfo Building with $o make "${o}=1" -B "${@}" || die einfo Build completed. done