1
0
Fork 0
mirror of https://github.com/yshui/picom.git synced 2025-04-14 17:53:25 -04:00

Merge branch 'next'

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2018-12-23 02:03:48 +00:00
commit 252bbaa94c
No known key found for this signature in database
GPG key ID: 37C999F617EA1A47
59 changed files with 7877 additions and 4903 deletions

98
.circleci/config.yml Normal file
View file

@ -0,0 +1,98 @@
defaults: &defaults
docker:
- image: yshui/comptonci
cached-checkout: &ccheckout
just-build: &build
version: 2.1
commands:
build:
parameters:
build-config:
type: string
default:
cc:
type: string
default: cc
steps:
- restore_cache:
keys:
- source-v1-{{ .Branch }}-{{ .Revision }}
- source-v1-{{ .Branch }}-
- source-v1-
- checkout
- save_cache:
key: source-v1-{{ .Branch }}-{{ .Revision }}
paths:
- ".git"
- run:
name: config
command: CC=<< parameters.cc >> meson << parameters.build-config >> -Dnew_backends=true --werror . build
- run:
name: build
command: ninja -C build
test-xvfb:
steps:
- run:
name: xxx
command: xvfb-run -s "-screen 0 640x480x24" glxinfo
jobs:
basic:
<<: *defaults
steps:
- build:
build-config: -Dbuild_docs=true
minimal:
<<: *defaults
steps:
- build:
build-config: -Dopengl=false -Ddbus=false -Dregex=false -Dconfig_file=false
nogl:
<<: *defaults
steps:
- build:
build-config: -Dopengl=false
noregex:
<<: *defaults
steps:
- build:
build-config: -Dregex=false
clang_basic:
<<: *defaults
steps:
- build:
cc: clang-6.0
build-config:
clang_minimal:
<<: *defaults
steps:
- build:
cc: clang-6.0
build-config: -Dopengl=false -Ddbus=false -Dregex=false -Dconfig_file=false
clang_nogl:
<<: *defaults
steps:
- build:
cc: clang-6.0
build-config: -Dopengl=false
clang_noregex:
<<: *defaults
steps:
- build:
cc: clang-6.0
build-config: -Dregex=false
workflows:
all_builds:
jobs:
- basic
- clang_basic
- minimal
- clang_minimal
- nogl
- clang_nogl
# - test-xvfb
# vim: set sw=2 ts=8 et:

View file

@ -2,3 +2,4 @@ root = true
[*.{c,h}]
indent_style = space
indent_size = 2
max_line_length = 90

View file

@ -32,6 +32,12 @@
### Current Behavior
### Stack trace
// If compton crashes, please make sure your compton is built with debug info, and provide a stack trace of compton when it crashed.
// Note, when compton crashes in a debugger, your screen might look frozen. But gdb will still handle your input if it is focused.
// Usually you can use 'bt' and press enter to get the stack trace, then 'q', enter, 'y', enter to quit gdb.
// Or, you can enable core dump, and upload the core file and the compton executable here.
### Other details
// If your problem is visual, you are encouraged to record a short video when the problem occurs and link to it here.

View file

@ -1,10 +1,12 @@
Sorted in alphabetical order
Adam Jackson <ajax@nwnk.net>
Avi-D-coder <avi.the.coder@gmail.com>
Brottweiler <tibell.christoffer@gmail.com>
Carl Worth <cworth@cworth.org>
Christopher Jeffrey <chjjeffrey@gmail.com>
Corax26 <cor.ax26@gmail.com>
Dan Elkouby <streetwalkermc@gmail.com>
Dana Jansens <danakj@orodu.net>
Dave Airlie <airlied@linux.ie>
dolio

197
Makefile
View file

@ -1,197 +0,0 @@
# 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 x11-xcb xcb-renderutil xcb-render xcb-damage xcb-randr xcb-composite xcb-shape xcb-image xcb-xfixes xext pixman-1
LIBS = -lm -lrt -lev
INCS =
OBJS = compton.o config.o win.o x.o
# === Configuration flags ===
CFG = -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wno-unused-parameter -Wnonnull
ifeq "$(CC)" "clang"
CFG += -Wconditional-uninitialized
endif
# ==== Xinerama ====
# Enables support for --xinerama-shadow-crop
ifeq "$(NO_XINERAMA)" ""
CFG += -DCONFIG_XINERAMA
PACKAGES += xcb-xinerama
endif
# ==== libconfig ====
# Enables configuration file parsing support
ifeq "$(NO_LIBCONFIG)" ""
CFG += -DCONFIG_LIBCONFIG
PACKAGES += libconfig
OBJS += config_libconfig.o
# libconfig-1.3* does not define LIBCONFIG_VER* macros, so we use
# pkg-config to determine its version here
LIBCONFIG_VER = $(shell pkg-config --atleast-version=1.4 libconfig; echo $$?)
ifeq ($LIBCONFIG_VER, 1)
$(error "Your libconfig is too old, at least 1.4 is required")
endif
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.
ifneq "$(NO_VSYNC_OPENGL)" ""
NO_OPENGL=$(NO_VSYNC_OPENGL)
endif
ifeq "$(NO_OPENGL)" ""
CFG += -DCONFIG_OPENGL
# -lGL must precede some other libraries, or it segfaults on FreeBSD (#74)
LIBS := -lGL $(LIBS)
OBJS += opengl.o
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
OBJS += c2.o
# ==== 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
BUILD_TYPE ?= "Debug"
ifeq "$(BUILD_TYPE)" "Release"
CFG += -DNDEBUG -O2 -D_FORTIFY_SOURCE=2
else ifeq "$(BUILD_TYPE)" "Debug"
CFG += -ggdb -Wshadow
endif
ifeq "$(ENABLE_SAN)" "1"
ifeq "$(CC)" "clang"
CFG += -fsanitize=address,undefined,integer,nullability
else
CFG += -fsanitize=address,undefined
endif
else ifeq "$(ENABLE_SAN)" "memory"
CFG += -fsanitize=memory
endif
LIBS += $(shell pkg-config --libs $(PACKAGES))
INCS += $(shell pkg-config --cflags $(PACKAGES))
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) $(INCS)); do echo "$$i"; done) > $@
.deps:
mkdir -p $@
.deps/%.d: src/%.c | .deps
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $(INCS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
%.o: src/%.c
$(CC) $(CFG) $(CPPFLAGS) $(INCS) -c src/$*.c -o $@
compton: $(OBJS)
$(CC) $(CFG) $(CPPFLAGS) $(LDFLAGS) -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
@rm -rf .deps
version:
@echo "$(COMPTON_VERSION)"
.PHONY: uninstall clean docs version
ifneq ($(MAKECMDGOALS),clean)
include $(addprefix .deps/,$(OBJS:.o=.d))
endif

View file

@ -1,10 +1,18 @@
Compton
=======
This is forked from the original Compton because that seems to have become unmaintained. I'll merge pull requests as they appear upstream, as well as trying to fix bugs reported to upstream, or found by myself.
This is forked from the original Compton because that seems to have become unmaintained.
The current battle plan of this fork is to refactor it to make the code _possible_ to maintain, so potential contributors won't be scared away when they take a look at the code.
We also try to fix bugs.
The original README can be found [here](README_orig.md)
## Changelog
See [Releases](https://github.com/yshui/compton/releases)
## Build
### Dependencies
@ -12,6 +20,7 @@ The original README can be found [here](README_orig.md)
Assuming you already have all the usual building tools installed (e.g. gcc, meson, ninja, etc.), you still need:
* libx11
* libx11-xcb
* libXext
* xproto
* xcb
@ -24,10 +33,11 @@ Assuming you already have all the usual building tools installed (e.g. gcc, meso
* xcb-composite
* xcb-image
* xcb-present
* xcb-xinerama (optional, disable with `-Dxinerama=false` meson configure flag)
* xcb-xinerama (optional, disable with the `-Dxinerama=false` meson configure flag)
* pixman
* libdbus (optional, disable with the `-Ddbus=false` meson configure flag)
* libconfig (optional, disable with the `-Dconfig_file=false` meson configure flag)
* libxdg-basedir (optional, disable with the `-Dconfig_file=false` meson configure flag)
* libGL (optional, disable with the `-Dopengl=false` meson configure flag)
* libpcre (optional, disable with the `-Dregex=false` meson configure flag)
* libev

View file

@ -1,11 +1,10 @@
# 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;
log-level = "warn";
# log-file = "/path/to/your/log/file";
# shadow-opacity = 0.7;
# shadow-red = 0.0;
# shadow-green = 0.0;
@ -22,12 +21,10 @@ shadow-exclude = [
# 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;
@ -60,8 +57,6 @@ 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;
@ -83,5 +78,9 @@ glx-swap-method = "undefined";
# Window type settings
wintypes:
{
tooltip = { fade = true; shadow = true; opacity = 0.75; focus = true; };
tooltip = { fade = true; shadow = true; opacity = 0.75; focus = true; full-shadow = false; };
dock = { shadow = false; }
dnd = { shadow = false; }
popup_menu = { opacity = 0.8; }
dropdown_menu = { opacity = 0.8; }
};

View file

@ -1,46 +0,0 @@
option(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)

View file

@ -2,8 +2,8 @@ compton-trans(1)
================
:doctype: manpage
:man source: compton
:man version: nightly-20121114
:man manual: LOCAL USER COMMANDS
:man version: {compton-version}
:man manual: User Commands
NAME
----
@ -77,11 +77,7 @@ compton-trans -c -- -5
BUGS
----
Please report any bugs you find to <https://github.com/chjj/compton> .
AUTHORS
-------
Christopher Jeffrey (<https://github.com/chjj>).
Please submit bug reports to <https://github.com/yshui/compton>.
SEE ALSO
--------

View file

@ -2,8 +2,8 @@ compton(1)
==========
:doctype: manpage
:man source: compton
:man version: nightly-20141124
:man manual: LOCAL USER COMMANDS
:man version: {compton-version}
:man manual: User Commands
NAME
----
@ -13,10 +13,6 @@ 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.
@ -26,9 +22,6 @@ 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)
@ -54,7 +47,7 @@ OPTIONS
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.
Enabled client-side shadows on windows. Note desktop windows (windows with '_NET_WM_WINDOW_TYPE_DESKTOP') never get shadow, unless explicitly requested using the wintypes option.
*-C*, *--no-dock-shadow*::
Avoid drawing shadows on dock/panel windows.
@ -77,8 +70,11 @@ OPTIONS
*-b*, *--daemon*::
Daemonize process. Fork to background after initialization. Causes issues with certain (badly-written) drivers.
*-S*::
Enable synchronous X operation (for debugging).
*--log-level*::
Set the log level. Possible values are "TRACE", "DEBUG", "INFO", "WARN", "ERROR", in increasing level of importance. Case doesn't matter.
*--log-file*::
Set the log file. If *--log-file* is never specified, logs will be written to stderr. Otherwise, logs will to writeen to the give file, though some of the early logs might still be written to the stderr. When setting this option from the config file, it is recommended to use an absolute path.
*--show-all-xerrors*::
Show all X errors (for debugging).
@ -139,8 +135,8 @@ OPTIONS
* '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.
* 'opengl-swc': Try to VSync with 'MESA_swap_control' or 'SGI_swap_control' (in order of preference) OpenGL extension. Works only with GLX backend. Known to be most effective on many drivers. Does not guarantee to control paint timing.
* 'opengl-mswc': Deprecated, use 'opengl-swc' instead.
(Note some VSync methods may not be enabled at compile time.)
--
@ -148,9 +144,6 @@ OPTIONS
*--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.
@ -372,11 +365,28 @@ compton uses general libconfig configuration file format. A sample configuration
------------
wintypes:
{
WINDOW_TYPE = { fade = BOOL; shadow = BOOL; opacity = FLOAT; focus = BOOL; };
WINDOW_TYPE = { fade = BOOL; shadow = BOOL; opacity = FLOAT; focus = BOOL; full-shadow = BOOL; redir-ignore = 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.)
'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".
Following per window-type options are available: ::
fade, 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.)
full-shadow:::
Controls whether shadow is drawn under the parts of the window that you normally won't be able to see. Useful when the window has parts of it transparent, and you want shadows in those areas.
redir-ignore:::
Controls whether this type of windows should cause screen to become redirected again after been unredirected. If you have *--unredir-if-possible* set, and doesn't want certain window to cause unnecessary screen redirection, you can set this to `true`.
SIGNALS
-------
@ -431,15 +441,13 @@ $ compton --backend glx --vsync opengl-swc
BUGS
----
Please report any you find to <https://github.com/chjj/compton> .
Please submit bug reports to <https://github.com/yshui/compton>.
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.
Out dated information in this man page is considered a bug.
RESOURCES
---------
Homepage: <https://github.com/chjj/compton>
Homepage: <https://github.com/yshui/compton>
SEE ALSO
--------

View file

@ -3,8 +3,10 @@ if get_option('build_docs')
a2x = find_program('a2x')
foreach m : mans
custom_target(m, output: [m], input: [m+'.asciidoc'],
command: [a2x, '--format', 'manpage', '@INPUT@',
'-D', meson.current_build_dir()],
command: [a2x, '-a',
'compton-version='+version,
'--format', 'manpage', '@INPUT@', '-D',
meson.current_build_dir()],
install: true, install_dir: 'share/man/man1/')
endforeach
endif

View file

@ -1,8 +1,12 @@
project('compton', 'c', version: '3')
project('compton', 'c', version: '4',
default_options: ['c_std=c11'])
cc = meson.get_compiler('c')
version = ''
# use project version by default
version = 'v'+meson.project_version()
# use git describe if that's available
git = find_program('git', required: false)
if git.found()
gitv = run_command('git', 'describe')
@ -11,16 +15,10 @@ if git.found()
endif
endif
if version == ''
# use project version by default
version = 'v'+meson.project_version()
endif
add_global_arguments('-std=c11', language: 'c')
add_global_arguments('-DCOMPTON_VERSION="'+version+'"', language: 'c')
if get_option('buildtype') == 'release'
add_global_arguments('-DNDEBUG', language: 'c')
add_global_arguments('-DNDEBUG', language: 'c')
endif
if get_option('sanitize')

View file

@ -11,3 +11,5 @@ option('dbus', type: 'boolean', value: true, description: 'Enable suport for D-B
option('xrescheck', type: 'boolean', value: false, description: 'Enable X resource leak checker (for debug only)')
option('build_docs', type: 'boolean', value: false, description: 'Build documentation and man pages')
option('new_backends', type: 'boolean', value: false, description: 'Does not really do anything right now')

11
src/backend/backend.c Normal file
View file

@ -0,0 +1,11 @@
#include "backend.h"
backend_info_t *backend_list[NUM_BKEND] = {[BKEND_XRENDER] = &xrender_backend};
bool default_is_win_transparent(void *backend_data, win *w, void *win_data) {
return w->mode != WMODE_SOLID;
}
bool default_is_frame_transparent(void *backend_data, win *w, void *win_data) {
return w->frame_opacity != 1;
}

130
src/backend/backend.h Normal file
View file

@ -0,0 +1,130 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2018, Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include "common.h"
#include "region.h"
typedef struct backend_info {
// =========== Initialization ===========
/// Initialize the backend, prepare for rendering to the target window.
/// Here is how you should choose target window:
/// 1) if ps->overlay is not XCB_NONE, use that
/// 2) use ps->root otherwise
/// XXX make the target window a parameter
void *(*init)(session_t *ps) __attribute__((nonnull(1)));
void (*deinit)(void *backend_data, session_t *ps) __attribute__((nonnull(1, 2)));
/// Called when rendering will be stopped for an unknown amount of
/// time (e.g. screen is unredirected). Free some resources.
void (*pause)(void *backend_data, session_t *ps);
/// Called before rendering is resumed
void (*resume)(void *backend_data, session_t *ps);
/// Called when root property changed, returns the new
/// backend_data. Even if the backend_data changed, all
/// the existing win_data returned by prepare_win should
/// remain valid.
///
/// Optional
void *(*root_change)(void *backend_data, session_t *ps);
// =========== Rendering ============
/// Called before any compose() calls.
///
/// Usually the backend should clear the buffer, or paint a background
/// on the buffer (usually the wallpaper).
///
/// Optional?
void (*prepare)(void *backend_data, session_t *ps, const region_t *reg_paint);
/// Paint the content of the window onto the (possibly buffered)
/// target picture. Always called after render_win(). Maybe called
/// multiple times between render_win() and finish_render_win().
/// The origin is the top left of the window, exclude the shadow,
/// (dst_x, dst_y) refers to where the origin should be in the target
/// buffer.
void (*compose)(void *backend_data, session_t *ps, win *w, void *win_data,
int dst_x, int dst_y, const region_t *reg_paint);
/// Blur a given region on of the target.
bool (*blur)(void *backend_data, session_t *ps, double opacity, const region_t *)
__attribute__((nonnull(1, 2, 4)));
/// Present the buffered target picture onto the screen. If target
/// is not buffered, this should be NULL.
///
/// Optional
void (*present)(void *backend_data, session_t *ps) __attribute__((nonnull(1, 2)));
/**
* Render the content of a window into an opaque
* data structure. Dimming, shadow and color inversion is handled
* here.
*
* This function is allowed to allocate additional resource needed
* for rendering.
*
* Params:
* reg_paint = the paint region, meaning painting should only
* be happening within that region. It's in global
* coordinates. If NULL, the region of paint is the
* whole screen.
*/
void (*render_win)(void *backend_data, session_t *ps, win *w, void *win_data,
const region_t *reg_paint);
/// Free resource allocated for rendering. After this function is
/// called, compose() won't be called before render_win is called
/// another time.
///
/// Optional
void (*finish_render_win)(void *backend_data, session_t *ps, win *w,
void *win_data);
// ============ Resource management ===========
// XXX Thoughts: calling release_win and prepare_win for every config notify
// is wasteful, since there can be multiple such notifies per drawing.
// But if we don't, it can mean there will be a state where is window is
// mapped and visible, but there is no win_data attached to it. We don't
// want to break that assumption.
/// Create a structure to stored additional data needed for rendering a
/// window, later used for render() and compose().
///
/// Backend can assume this function will only be called with visible
/// InputOutput windows, and only be called when screen is redirected.
///
/// Backend can assume size, shape and visual of the window won't change between
/// prepare_win() and release_win().
void *(*prepare_win)(void *backend_data, session_t *ps, win *w)
__attribute__((nonnull(1, 2, 3)));
/// Free resources allocated by prepare()
void (*release_win)(void *backend_data, session_t *ps, win *w, void *win_data)
__attribute__((nonnull(1, 2, 3)));
// =========== Query ===========
/// Return if a window has transparent content. Guaranteed to only
/// be called after render_win is called.
bool (*is_win_transparent)(void *backend_data, win *w, void *win_data)
__attribute__((nonnull(1, 2)));
/// Return if the frame window has transparent content. Guaranteed to
/// only be called after render_win is called.
bool (*is_frame_transparent)(void *backend_data, win *w, void *win_data)
__attribute__((nonnull(1, 2)));
} backend_info_t;
extern backend_info_t xrender_backend;
extern backend_info_t glx_backend;
extern backend_info_t *backend_list[NUM_BKEND];
bool default_is_win_transparent(void *, win *, void *);
bool default_is_frame_transparent(void *, win *, void *);

View file

@ -0,0 +1,116 @@
#include <xcb/xcb_image.h>
#include "render.h"
#include "backend_common.h"
/**
* Generate a 1x1 <code>Picture</code> of a particular color.
*/
xcb_render_picture_t
solid_picture(session_t *ps, bool argb, double a, double r, double g, double b) {
xcb_pixmap_t pixmap;
xcb_render_picture_t picture;
xcb_render_create_picture_value_list_t pa;
xcb_render_color_t col;
xcb_rectangle_t rect;
pixmap = x_create_pixmap(ps, argb ? 32 : 8, ps->root, 1, 1);
if (!pixmap)
return None;
pa.repeat = True;
picture = x_create_picture_with_standard_and_pixmap(
ps, argb ? XCB_PICT_STANDARD_ARGB_32 : XCB_PICT_STANDARD_A_8, pixmap,
XCB_RENDER_CP_REPEAT, &pa);
if (!picture) {
xcb_free_pixmap(ps->c, pixmap);
return None;
}
col.alpha = a * 0xffff;
col.red = r * 0xffff;
col.green = g * 0xffff;
col.blue = b * 0xffff;
rect.x = 0;
rect.y = 0;
rect.width = 1;
rect.height = 1;
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, picture, col, 1, &rect);
xcb_free_pixmap(ps->c, pixmap);
return picture;
}
/**
* Generate shadow <code>Picture</code> for a window.
*/
bool build_shadow(session_t *ps, double opacity, const int width, const int height,
xcb_render_picture_t shadow_pixel, xcb_pixmap_t *pixmap,
xcb_render_picture_t *pict) {
xcb_image_t *shadow_image = NULL;
xcb_pixmap_t shadow_pixmap = None, shadow_pixmap_argb = None;
xcb_render_picture_t shadow_picture = None, shadow_picture_argb = None;
xcb_gcontext_t gc = None;
shadow_image = make_shadow(ps, opacity, width, height);
if (!shadow_image) {
log_error("Failed to make shadow");
return false;
}
shadow_pixmap =
x_create_pixmap(ps, 8, ps->root, shadow_image->width, shadow_image->height);
shadow_pixmap_argb =
x_create_pixmap(ps, 32, ps->root, shadow_image->width, shadow_image->height);
if (!shadow_pixmap || !shadow_pixmap_argb) {
log_error("Failed to create shadow pixmaps");
goto shadow_picture_err;
}
shadow_picture = x_create_picture_with_standard_and_pixmap(
ps, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL);
shadow_picture_argb = x_create_picture_with_standard_and_pixmap(
ps, XCB_PICT_STANDARD_ARGB_32, shadow_pixmap_argb, 0, NULL);
if (!shadow_picture || !shadow_picture_argb)
goto shadow_picture_err;
gc = xcb_generate_id(ps->c);
xcb_create_gc(ps->c, gc, shadow_pixmap, 0, NULL);
xcb_image_put(ps->c, shadow_pixmap, gc, shadow_image, 0, 0, 0);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, shadow_pixel, shadow_picture,
shadow_picture_argb, 0, 0, 0, 0, 0, 0, shadow_image->width,
shadow_image->height);
*pixmap = shadow_pixmap_argb;
*pict = shadow_picture_argb;
xcb_free_gc(ps->c, gc);
xcb_image_destroy(shadow_image);
xcb_free_pixmap(ps->c, shadow_pixmap);
xcb_render_free_picture(ps->c, shadow_picture);
return true;
shadow_picture_err:
if (shadow_image)
xcb_image_destroy(shadow_image);
if (shadow_pixmap)
xcb_free_pixmap(ps->c, shadow_pixmap);
if (shadow_pixmap_argb)
xcb_free_pixmap(ps->c, shadow_pixmap_argb);
if (shadow_picture)
xcb_render_free_picture(ps->c, shadow_picture);
if (shadow_picture_argb)
xcb_render_free_picture(ps->c, shadow_picture_argb);
if (gc)
xcb_free_gc(ps->c, gc);
return false;
}
// vim: set noet sw=8 ts=8 :

View file

@ -0,0 +1,10 @@
#pragma once
#include <xcb/xcb_image.h>
#include "common.h"
bool build_shadow(session_t *ps, double opacity, const int width, const int height,
xcb_render_picture_t shadow_pixel, xcb_pixmap_t *pixmap,
xcb_render_picture_t *pict);
xcb_render_picture_t
solid_picture(session_t *ps, bool argb, double a, double r, double g, double b);

575
src/backend/gl/gl_common.c Normal file
View file

@ -0,0 +1,575 @@
#include <GL/gl.h>
#include <GL/glext.h>
#include <stdbool.h>
#include "common.h"
#include "log.h"
#include "backend/gl/gl_common.h"
GLuint gl_create_shader(GLenum shader_type, const char *shader_str) {
log_trace("===\n%s\n===", shader_str);
bool success = false;
GLuint shader = glCreateShader(shader_type);
if (!shader) {
log_error("Failed to create shader with type %#x.", shader_type);
goto 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);
log_error("Failed to compile shader with type %d: %s",
shader_type, log);
}
goto end;
}
}
success = true;
end:
if (shader && !success) {
glDeleteShader(shader);
shader = 0;
}
return shader;
}
GLuint gl_create_program(const GLuint *const shaders, int nshaders) {
bool success = false;
GLuint program = glCreateProgram();
if (!program) {
log_error("Failed to create program.");
goto 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);
log_error("Failed to link program: %s", log);
}
goto end;
}
}
success = true;
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
gl_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 = gl_create_shader(GL_VERTEX_SHADER, vert_shader_str);
if (frag_shader_str)
frag_shader = gl_create_shader(GL_FRAGMENT_SHADER, frag_shader_str);
{
GLuint shaders[2];
unsigned 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 = gl_create_program(shaders, count);
}
if (vert_shader)
glDeleteShader(vert_shader);
if (frag_shader)
glDeleteShader(frag_shader);
return prog;
}
/**
* @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 *gl_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 = ccalloc(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;
}
/**
* @brief Render a region with texture data.
*/
bool gl_compose(const gl_texture_t *ptex, int x, int y, int dx, int dy, int width,
int height, int z, double opacity, bool argb, bool neg,
const region_t *reg_tgt, const gl_win_shader_t *shader) {
if (!ptex || !ptex->texture) {
log_error("Missing texture.");
return false;
}
// argb = argb || (GLX_TEXTURE_FORMAT_RGBA_EXT ==
// ps->psglx->fbconfigs[ptex->depth]->texture_fmt);
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);
}
// Programmable path
assert(shader->prog);
glUseProgram(shader->prog);
if (shader->unifm_opacity >= 0)
glUniform1f(shader->unifm_opacity, opacity);
if (shader->unifm_invert_color >= 0)
glUniform1i(shader->unifm_invert_color, neg);
if (shader->unifm_tex >= 0)
glUniform1i(shader->unifm_tex, 0);
// log_trace("Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n",
// x, y, width, height, dx, dy, ptex->width, ptex->height, z);
// 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(crect) {
// Calculate texture coordinates
GLfloat texture_x1 = (double)(crect.x1 - dx + x);
GLfloat texture_y1 = (double)(crect.y1 - dy + y);
GLfloat texture_x2 = texture_x1 + (double)(crect.x2 - crect.x1);
GLfloat texture_y2 = texture_y1 + (double)(crect.y2 - crect.y1);
if (GL_TEXTURE_2D == ptex->target) {
// GL_TEXTURE_2D coordinates are 0-1
texture_x1 /= ptex->width;
texture_y1 /= ptex->height;
texture_x2 /= ptex->width;
texture_y2 /= ptex->height;
}
// Vertex coordinates
GLint vx1 = crect.x1;
GLint vy1 = crect.y1;
GLint vx2 = crect.x2;
GLint vy2 = crect.y2;
// X pixmaps might be Y inverted, invert the texture coordinates
if (ptex->y_inverted) {
texture_y1 = 1.0 - texture_y1;
texture_y2 = 1.0 - texture_y2;
}
// log_trace("Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d",
// ri, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye);
GLfloat texture_x[] = {texture_x1, texture_x2, texture_x2, texture_x1};
GLfloat texture_y[] = {texture_y1, texture_y1, texture_y2, texture_y2};
GLint vx[] = {vx1, vx2, vx2, vx1};
GLint vy[] = {vy1, vy1, vy2, vy2};
for (int i = 0; i < 4; i++) {
glTexCoord2f(texture_x[i], texture_y[i]);
glVertex3i(vx[i], vy[i], 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);
}
glUseProgram(0);
gl_check_err();
return true;
}
bool gl_dim_reg(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor, const region_t *reg_tgt) {
// 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(crect) {
glVertex3i(crect.x1, crect.y1, z);
glVertex3i(crect.x2, crect.y1, z);
glVertex3i(crect.x2, crect.y2, z);
glVertex3i(crect.x1, crect.y2, z);
}
P_PAINTREG_END();
}
glEnd();
glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
glDisable(GL_BLEND);
gl_check_err();
return true;
}
static inline int gl_gen_texture(GLenum tex_tgt, int width, int height, GLuint *tex) {
glGenTextures(1, tex);
if (!*tex)
return -1;
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 0;
}
/**
* Blur contents in a particular region.
*
* XXX seems to be way to complex for what it does
*/
// Blur the area sized width x height starting at dx x dy
bool gl_blur_dst(session_t *ps, const gl_cap_t *cap, int dx, int dy, int width,
int height, float z, GLfloat factor_center, const region_t *reg_tgt,
gl_blur_cache_t *pbc, const gl_blur_shader_t *pass, int npasses) {
const bool more_passes = npasses > 1;
// these should be arguments
const bool have_scissors = glIsEnabled(GL_SCISSOR_TEST);
const bool have_stencil = glIsEnabled(GL_STENCIL_TEST);
bool ret = false;
// Calculate copy region size
gl_blur_cache_t ibc = {.width = 0, .height = 0};
if (!pbc)
pbc = &ibc;
// log_trace("(): %d, %d, %d, %d\n", dx, dy, width, height);
GLenum tex_tgt = GL_TEXTURE_RECTANGLE;
if (cap->non_power_of_two_texture)
tex_tgt = GL_TEXTURE_2D;
// Free textures if size inconsistency discovered
if (width != pbc->width || height != pbc->height) {
glDeleteTextures(1, &pbc->textures[0]);
glDeleteTextures(1, &pbc->textures[1]);
pbc->width = pbc->height = 0;
pbc->textures[0] = pbc->textures[1] = 0;
}
// Generate FBO and textures if needed
if (!pbc->textures[0])
gl_gen_texture(tex_tgt, width, height, &pbc->textures[0]);
GLuint tex_scr = pbc->textures[0];
if (npasses > 1 && !pbc->textures[1])
gl_gen_texture(tex_tgt, width, height, &pbc->textures[1]);
pbc->width = width;
pbc->height = height;
GLuint tex_scr2 = pbc->textures[1];
if (npasses > 1 && !pbc->fbo)
glGenFramebuffers(1, &pbc->fbo);
const GLuint fbo = pbc->fbo;
if (!tex_scr || (npasses > 1 && !tex_scr2)) {
log_error("Failed to allocate texture.");
goto end;
}
if (npasses > 1 && !fbo) {
log_error("Failed to allocate framebuffer.");
goto end;
}
// Read destination pixels into a texture
glEnable(tex_tgt);
glBindTexture(tex_tgt, tex_scr);
// Copy the area to be blurred into tmp buffer
glCopyTexSubImage2D(tex_tgt, 0, 0, 0, dx, dy, width, height);
// Texture scaling factor
GLfloat texfac_x = 1.0f, texfac_y = 1.0f;
if (tex_tgt == GL_TEXTURE_2D) {
texfac_x /= width;
texfac_y /= height;
}
// Paint it back
if (more_passes) {
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
}
for (int i = 0; i < npasses; ++i) {
assert(i < MAX_BLUR_PASS - 1);
const gl_blur_shader_t *curr = &pass[i];
assert(curr->prog);
assert(tex_scr);
glBindTexture(tex_tgt, tex_scr);
if (i < npasses - 1) {
// not last pass, draw into framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// XXX not fixing bug during porting
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, tex_scr2,
0); // XXX wrong, should use tex_tgt
glDrawBuffers(1, (GLenum[]){GL_COLOR_ATTACHMENT0});
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
GL_FRAMEBUFFER_COMPLETE) {
log_error("Framebuffer attachment failed.");
goto end;
}
} else {
// last pass, draw directly into the back buffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDrawBuffers(1, (GLenum[]){GL_BACK});
if (have_scissors)
glEnable(GL_SCISSOR_TEST);
if (have_stencil)
glEnable(GL_STENCIL_TEST);
}
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glUseProgram(curr->prog);
if (curr->unifm_offset_x >= 0)
glUniform1f(curr->unifm_offset_x, texfac_x);
if (curr->unifm_offset_y >= 0)
glUniform1f(curr->unifm_offset_y, texfac_y);
if (curr->unifm_factor_center >= 0)
glUniform1f(curr->unifm_factor_center, factor_center);
// XXX use multiple draw calls is probably going to be slow than
// just simply blur the whole area.
P_PAINTREG_START(crect) {
// Texture coordinates
const GLfloat texture_x1 = (crect.x1 - dx) * texfac_x;
const GLfloat texture_y1 = (crect.y1 - dy) * texfac_y;
const GLfloat texture_x2 =
texture_x1 + (crect.x2 - crect.x1) * texfac_x;
const GLfloat texture_y2 =
texture_y1 + (crect.y2 - crect.y1) * texfac_y;
// Vertex coordinates
// For passes before the last one, we are drawing into a buffer,
// so (dx, dy) from source maps to (0, 0)
GLfloat vx1 = crect.x1 - dx;
GLfloat vy1 = crect.y1 - dy;
if (i == npasses - 1) {
// For last pass, we are drawing back to source, so we
// don't need to map
vx1 = crect.x1;
vy1 = crect.y1;
}
GLfloat vx2 = vx1 + (crect.x2 - crect.x1);
GLfloat vy2 = vy1 + (crect.y2 - crect.y1);
GLfloat texture_x[] = {texture_x1, texture_x2, texture_x2,
texture_x1};
GLfloat texture_y[] = {texture_y1, texture_y1, texture_y2,
texture_y2};
GLint vx[] = {vx1, vx2, vx2, vx1};
GLint vy[] = {vy1, vy1, vy2, vy2};
for (int j = 0; j < 4; j++) {
glTexCoord2f(texture_x[j], texture_y[j]);
glVertex3i(vx[j], vy[j], 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;
end:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(tex_tgt, 0);
glDisable(tex_tgt);
if (have_scissors)
glEnable(GL_SCISSOR_TEST);
if (have_stencil)
glEnable(GL_STENCIL_TEST);
if (&ibc == pbc) {
glDeleteTextures(1, &pbc->textures[0]);
glDeleteTextures(1, &pbc->textures[1]);
glDeleteFramebuffers(1, &pbc->fbo);
}
gl_check_err();
return ret;
}
/**
* Set clipping region on the target window.
*/
void gl_set_clip(const region_t *reg) {
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
if (!reg)
return;
int nrects;
const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
if (nrects == 1) {
glEnable(GL_SCISSOR_TEST);
glScissor(rects[0].x1, rects[0].y2, rects[0].x2 - rects[0].x1,
rects[0].y2 - rects[0].y1);
}
gl_check_err();
}
static GLint glGetUniformLocationChecked(GLint prog, const char *name) {
GLint ret = glGetUniformLocation(prog, name);
if (ret < 0)
log_error("Failed to get location of uniform '%s'. Might be troublesome.",
name);
return ret;
}
/**
* Load a GLSL main program from shader strings.
*/
int gl_win_shader_from_string(session_t *ps, const char *vshader_str,
const char *fshader_str, gl_win_shader_t *ret) {
// Build program
ret->prog = gl_create_program_from_str(vshader_str, fshader_str);
if (!ret->prog) {
log_error("Failed to create GLSL program.");
return -1;
}
// Get uniform addresses
ret->unifm_opacity = glGetUniformLocationChecked(ret->prog, "opacity");
ret->unifm_invert_color = glGetUniformLocationChecked(ret->prog, "invert_color");
ret->unifm_tex = glGetUniformLocationChecked(ret->prog, "tex");
gl_check_err();
return true;
}
/**
* Callback to run on root window size change.
*/
void gl_resize(int width, int height) {
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, 0, height, -1000.0, 1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
static void attr_unused gl_destroy_win_shader(session_t *ps, gl_win_shader_t *shader) {
assert(shader);
assert(shader->prog);
glDeleteProgram(shader->prog);
shader->prog = 0;
shader->unifm_opacity = -1;
shader->unifm_invert_color = -1;
shader->unifm_tex = -1;
}

160
src/backend/gl/gl_common.h Normal file
View file

@ -0,0 +1,160 @@
#pragma once
#include <GL/gl.h>
#include <GL/glext.h>
#include "common.h"
// Program and uniforms for window shader
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;
} gl_win_shader_t;
// Program and uniforms for blur shader
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;
} gl_blur_shader_t;
/// @brief Wrapper of a binded GLX texture.
typedef struct gl_texture {
GLuint texture;
GLenum target;
unsigned width;
unsigned height;
unsigned depth;
bool y_inverted;
} gl_texture_t;
// OpenGL capabilities
typedef struct gl_cap {
bool non_power_of_two_texture;
} gl_cap_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;
} gl_blur_cache_t;
#define GL_PROG_MAIN_INIT \
{ .prog = 0, .unifm_opacity = -1, .unifm_invert_color = -1, .unifm_tex = -1, }
GLuint gl_create_shader(GLenum shader_type, const char *shader_str);
GLuint gl_create_program(const GLuint *const shaders, int nshaders);
GLuint
gl_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str);
bool gl_load_prog_main(session_t *ps, const char *vshader_str, const char *fshader_str,
gl_win_shader_t *pprogram);
unsigned char *gl_take_screenshot(session_t *ps, int *out_length);
void gl_resize(int width, int height);
/**
* Get a textual representation of an OpenGL error.
*/
static inline const char *gl_get_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 gl_check_err_(const char *func, int line) {
GLenum err = GL_NO_ERROR;
while (GL_NO_ERROR != (err = glGetError())) {
const char *errtext = gl_get_err_str(err);
if (errtext) {
log_printf(tls_logger, LOG_LEVEL_ERROR, func,
"GLX error at line %d: %s", line, errtext);
} else {
log_printf(tls_logger, LOG_LEVEL_ERROR, func,
"GLX error at line %d: %d", line, err);
}
}
}
#define gl_check_err() gl_check_err_(__func__, __LINE__)
/**
* Check if a GLX extension exists.
*/
static inline bool gl_has_extension(session_t *ps, const char *ext) {
GLint nexts = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &nexts);
if (!nexts) {
log_error("Failed get GL extension list.");
return false;
}
for (int i = 0; i < nexts; i++) {
const char *exti = (const char *)glGetStringi(GL_EXTENSIONS, i);
if (strcmp(ext, exti) == 0)
return true;
}
log_info("Missing GL extension %s.", ext);
return false;
}
static inline void gl_free_blur_shader(gl_blur_shader_t *shader) {
if (shader->prog)
glDeleteShader(shader->prog);
if (shader->frag_shader)
glDeleteShader(shader->frag_shader);
shader->prog = 0;
shader->frag_shader = 0;
}
#define P_PAINTREG_START(var) \
do { \
region_t reg_new; \
int nrects; \
const rect_t *rects; \
pixman_region32_init_rect(&reg_new, dx, dy, width, height); \
pixman_region32_intersect(&reg_new, &reg_new, (region_t *)reg_tgt); \
rects = pixman_region32_rectangles(&reg_new, &nrects); \
glBegin(GL_QUADS); \
\
for (int ri = 0; ri < nrects; ++ri) { \
rect_t var = rects[ri];
#define P_PAINTREG_END() \
} \
glEnd(); \
pixman_region32_fini(&reg_new); \
} \
while (0)

882
src/backend/gl/glx.c Normal file
View file

@ -0,0 +1,882 @@
// SPDX-License-Identifier: MIT
/*
* Compton - a compositor for X11
*
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
*
* Copyright (c) 2011-2013, Christopher Jeffrey
* See LICENSE-mit for more information.
*
*/
#include <GL/glx.h>
#include "backend/gl/glx.h"
#include "backend/backend.h"
#include "backend/gl/gl_common.h"
/// @brief Wrapper of a GLX FBConfig.
typedef struct {
GLXFBConfig cfg;
GLint texture_fmt;
GLint texture_tgts;
bool y_inverted;
} glx_fbconfig_t;
struct _glx_win_data {
gl_texture_t texture;
GLXPixmap glpixmap;
xcb_pixmap_t pixmap;
};
struct _glx_data {
int glx_event;
int glx_error;
GLXContext ctx;
gl_cap_t cap;
gl_blur_shader_t blur_shader[MAX_BLUR_PASS];
void (*glXBindTexImage)(Display *display, GLXDrawable drawable, int buffer,
const int *attrib_list);
void (*glXReleaseTexImage)(Display *display, GLXDrawable drawable, int buffer);
};
/**
* Check if a GLX extension exists.
*/
static inline bool glx_has_extension(session_t *ps, const char *ext) {
const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr);
if (!glx_exts) {
log_error("Failed get GLX extension list.");
return false;
}
int len = strlen(ext);
char *found = strstr(glx_exts, ext);
if (!found)
log_info("Missing GLX extension %s.", ext);
// Make sure extension names are not crazy...
assert(found[len] == ' ' || found[len] == 0);
return found != NULL;
}
/**
* @brief Release binding of a texture.
*/
void glx_release_pixmap(struct _glx_data *gd, Display *dpy, struct _glx_win_data *wd) {
// Release binding
if (wd->glpixmap && wd->texture.texture) {
glBindTexture(wd->texture.target, wd->texture.texture);
gd->glXReleaseTexImage(dpy, wd->glpixmap, GLX_FRONT_LEFT_EXT);
glBindTexture(wd->texture.target, 0);
}
// Free GLX Pixmap
if (wd->glpixmap) {
glXDestroyPixmap(dpy, wd->glpixmap);
wd->glpixmap = 0;
}
gl_check_err();
}
/**
* Free a glx_texture_t.
*/
static void glx_release_win(struct _glx_data *gd, Display *dpy, gl_texture_t *ptex) {
glx_release_pixmap(gd, dpy, ptex);
glDeleteTextures(1, &ptex->texture);
// Free structure itself
free(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);*/
/*free_glx_bc(ps, &w->glx_blur_cache);*/
}
>>>>>>> 4bc5ef8... wip glx backend:src/backend/gl/glx.c
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;
int tmpattr;
// Avoid 10-bit colors
glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, GLX_RED_SIZE, &tmpattr);
if (tmpattr != 8)
return -1;
glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, GLX_RED_SIZE, &tmpattr);
if (tmpattr != 8)
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;
}
/**
* @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) {
log_debug(
"(depth %d): %p overrides %p, target %#x.\n", depth, pfbcfg->cfg,
ps->psglx->fbconfigs[depth] ? ps->psglx->fbconfigs[depth]->cfg : 0,
pfbcfg->texture_tgts);
if (!ps->psglx->fbconfigs[depth]) {
ps->psglx->fbconfigs[depth] = cmalloc(glx_fbconfig_t);
}
(*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)) {
log_error("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)) {
log_error("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...
// log_error("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]) {
log_error("No FBConfig found for default depth %d.", ps->depth);
return false;
}
if (!ps->psglx->fbconfigs[32]) {
log_error("No FBConfig found for depth 32. Expect crazy things.");
}
log_trace("%d-bit: %p, 32-bit: %p", ps->depth,
ps->psglx->fbconfigs[ps->depth]->cfg, ps->psglx->fbconfigs[32]->cfg);
return true;
}
#ifdef DEBUG_GLX_DEBUG_CONTEXT
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;
}
static void
glx_debug_msg_callback(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar *message, GLvoid *userParam) {
log_trace("(): source 0x%04X, type 0x%04X, id %u, severity 0x%0X, \"%s\"", source,
type, id, severity, message);
}
#endif
/**
* Destroy GLX related resources.
*/
void glx_deinit(void *backend_data, session_t *ps) {
struct _glx_data *gd = backend_data;
// Free all GLX resources of windows
for (win *w = ps->list; w; w = w->next)
free_win_res_glx(ps, w);
// Free GLSL shaders/programs
for (int i = 0; i < MAX_BLUR_PASS; ++i) {
gl_free_blur_shader(&gd->blur_shader[i]);
}
glx_free_prog_main(ps, &ps->o.glx_prog_win);
gl_check_err();
// 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 (gd->ctx) {
glXDestroyContext(ps->dpy, gd->ctx);
gd->ctx = 0;
}
free(gd);
}
/**
* Initialize OpenGL.
*/
void *glx_init(session_t *ps) {
bool success = false;
auto gd = ccalloc(1, struct _glx_data);
XVisualInfo *pvis = NULL;
// Check for GLX extension
if (!glXQueryExtension(ps->dpy, &gd->glx_event, &gd->glx_error)) {
log_error("No GLX extension.");
goto end;
}
// Get XVisualInfo
int nitems = 0;
XVisualInfo vreq = {.visualid = ps->vis};
pvis = XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
if (!pvis) {
log_error("Failed to acquire XVisualInfo for current visual.");
goto end;
}
// Ensure the visual is double-buffered
int value = 0;
if (glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) {
log_error("Root visual is not a GL visual.");
goto end;
}
if (glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) {
log_error("Root visual is not a double buffered GL visual.");
goto end;
}
// Ensure GLX_EXT_texture_from_pixmap exists
if (!glx_has_extension(ps, "GLX_EXT_texture_from_pixmap"))
goto end;
// Initialize GLX data structure
for (int i = 0; i < MAX_BLUR_PASS; ++i) {
gd->blur_shader[i] = (gl_blur_shader_t){.frag_shader = -1,
.prog = -1,
.unifm_offset_x = -1,
.unifm_offset_y = -1,
.unifm_factor_center = -1};
}
// Get GLX context
gd->ctx = glXCreateContext(ps->dpy, pvis, None, GL_TRUE);
if (!gd->ctx) {
log_error("Failed to get GLX context.");
goto end;
}
// Attach GLX context
GLXDrawable tgt = ps->overlay;
if (!tgt) {
tgt = ps->root;
}
if (!glXMakeCurrent(ps->dpy, tgt, gd->ctx)) {
log_error("Failed to attach GLX context.");
goto end;
}
#ifdef DEBUG_GLX_DEBUG_CONTEXT
f_DebugMessageCallback p_DebugMessageCallback =
(f_DebugMessageCallback)glXGetProcAddress((const GLubyte *)"glDebugMessageCal"
"lback");
if (!p_DebugMessageCallback) {
log_error("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 (!ps->o.glx_no_stencil) {
GLint val = 0;
glGetIntegerv(GL_STENCIL_BITS, &val);
if (!val) {
log_error("Target window doesn't have stencil buffer.");
goto end;
}
}
// Check GL_ARB_texture_non_power_of_two, requires a GLX context and
// must precede FBConfig fetching
gd->cap.non_power_of_two_texture = gl_has_extension(ps, "GL_ARB_texture_non_"
"power_of_two");
// Acquire function addresses
#if 0
psglx->glStringMarkerGREMEDY = (f_StringMarkerGREMEDY)
glXGetProcAddress((const GLubyte *) "glStringMarkerGREMEDY");
psglx->glFrameTerminatorGREMEDY = (f_FrameTerminatorGREMEDY)
glXGetProcAddress((const GLubyte *) "glFrameTerminatorGREMEDY");
#endif
gd->glXBindTexImage = (void *)glXGetProcAddress((const GLubyte *)"glXBindTexImage"
"EXT");
gd->glXReleaseTexImage = (void *)glXGetProcAddress((const GLubyte *)"glXReleaseTe"
"xImageEXT");
if (!gd->glXBindTexImage || !gd->glXReleaseTexImage) {
log_error("Failed to acquire glXBindTexImageEXT() and/or "
"glXReleaseTexImageEXT(), make sure your OpenGL supports"
"GLX_EXT_texture_from_pixmap");
goto end;
}
// Acquire FBConfigs
if (!glx_update_fbconfig(ps))
goto end;
// Render preparations
gl_resize(ps->root_width, ps->root_height);
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;
end:
cxfree(pvis);
if (!success) {
glx_deinit(gd, ps);
return NULL;
}
return gd;
}
/**
* 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]) {
// Try to generate a framebuffer
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
if (!fbo) {
log_error("Failed to generate Framebuffer. Cannot do "
"multi-pass blur with GLX backend.");
return false;
}
glDeleteFramebuffers(1, &fbo);
}
{
char *lc_numeric_old = strdup(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 = strdup("");
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) {
xcb_render_fixed_t *kern = ps->o.blur_kerns[i];
if (!kern)
break;
glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i];
// Build shader
{
int wid = XFIXED_TO_DOUBLE(kern[0]),
hei = XFIXED_TO_DOUBLE(kern[1]);
int nele = wid * hei - 1;
unsigned 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 = ccalloc(len, char);
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 = XFIXED_TO_DOUBLE(
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) {
log_error("Failed to create fragment shader %d.", i);
return false;
}
// Build program
ppass->prog = glx_create_program(&ppass->frag_shader, 1);
if (!ppass->prog) {
log_error("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) { \
log_error("Failed to get location of %d-th uniform '" name "'. " \
"Mig" \
"ht " \
"be " \
"tro" \
"ubl" \
"eso" \
"me" \
".", \
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;
}
/**
* Bind a X pixmap to an OpenGL texture.
*/
bool glx_render_win(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
unsigned width, unsigned height, unsigned depth) {
if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID)
return true;
if (!pixmap) {
log_error("Binding to an empty pixmap %#010x. 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 = cmalloc(glx_texture_t);
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)) {
log_error("Failed to query info of pixmap %#010x.", pixmap);
return false;
}
if (depth > OPENGL_MAX_DEPTH) {
log_error("Requested depth %d higher than max possible "
"depth %d.",
depth, OPENGL_MAX_DEPTH);
return false;
}
}
const glx_fbconfig_t *pcfg = ps->psglx->fbconfigs[depth];
if (!pcfg) {
log_error("Couldn't find FBConfig with requested depth %d", depth);
return false;
}
// Choose a suitable texture target for our pixmap.
// Refer to GLX_EXT_texture_om_pixmap spec to see what are the mean
// of the bits in texture_tgts
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;
log_debug("depth %d, tgt %#x, rgba %d\n", depth, tex_tgt,
(GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt));
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) {
log_error("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) {
log_error("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;
}
#if 0
/**
* Preprocess function before start painting.
*/
void
glx_paint_pre(session_t *ps, region_t *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
region_t newdamage;
pixman_region32_init(&newdamage);
if (trace_damage)
copy_region(&newdamage, preg);
// We use GLX buffer_age extension to decide which pixels in
// the back buffer is reusable, and limit our redrawing
int buffer_age = 0;
// Query GLX_EXT_buffer_age for buffer age
if (ps->o.glx_swap_method == SWAPM_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;
assert(buffer_age >= 0);
if (buffer_age) {
// Determine paint area
for (int i = 0; i < buffer_age - 1; ++i)
pixman_region32_union(preg, preg, &ps->all_damage_last[i]);
} else
// buffer_age == 0 means buffer age is not available, paint everything
copy_region(preg, &ps->screen_reg);
if (trace_damage) {
// XXX use a circular queue instead of memmove
pixman_region32_fini(&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(region_t *));
ps->all_damage_last[0] = newdamage;
}
glx_set_clip(ps, preg);
#ifdef DEBUG_GLX_PAINTREG
glx_render_color(ps, 0, 0, ps->root_width, ps->root_height, 0, *preg, NULL);
#endif
glx_check_err(ps);
}
#endif
backend_info_t glx_backend = {
.init = glx_init,
};

50
src/backend/gl/glx.h Normal file
View file

@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
/*
* Compton - a compositor for X11
*
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
*
* Copyright (c) 2011-2013, Christopher Jeffrey
* See LICENSE-mit for more information.
*
*/
#pragma once
#include <ctype.h>
#include <locale.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <assert.h>
#include <stddef.h>
#include "common.h"
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_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t 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, region_t *preg)
__attribute__((nonnull(1, 2)));
/**
* Check if a texture is binded, or is binded to the given pixmap.
*/
static inline bool
glx_tex_binded(const glx_texture_t *ptex, xcb_pixmap_t pixmap) {
return ptex && ptex->glpixmap && ptex->texture
&& (!pixmap || pixmap == ptex->pixmap);
}

12
src/backend/meson.build Normal file
View file

@ -0,0 +1,12 @@
# enable xrender
if get_option('new_backends')
srcs += [ files('xrender.c', 'backend.c', 'backend_common.c') ]
# enable opengl
if get_option('opengl')
srcs += [ files('gl/gl_common.c') ]
deps += [ dependency('gl', required: true) ]
cflags += [ '-DGL_GLEXT_PROTOTYPES' ]
endif
endif

464
src/backend/xrender.c Normal file
View file

@ -0,0 +1,464 @@
#include <assert.h>
#include <math.h>
#include "backend/backend.h"
#include "backend_common.h"
#include "utils.h"
#include "win.h"
#define auto __auto_type
typedef struct _xrender_data {
/// The painting target drawable
xcb_drawable_t target_draw;
/// The painting target, it is either the root or the overlay
xcb_render_picture_t target;
/// A buffer of the image to paint
xcb_render_picture_t target_buffer;
/// The original root window content, usually the wallpaper.
/// We save it so we don't loss the wallpaper when we paint over
/// it.
xcb_render_picture_t root_pict;
/// Pictures of pixel of different alpha value, used as a mask to
/// paint transparent images
xcb_render_picture_t alpha_pict[256];
// XXX don't know if these are really needed
/// 1x1 white picture
xcb_render_picture_t white_pixel;
/// 1x1 black picture
xcb_render_picture_t black_pixel;
/// 1x1 picture of the shadow color
xcb_render_picture_t shadow_pixel;
} xrender_data;
#if 0
/**
* Paint root window content.
*/
static void
paint_root(session_t *ps, const region_t *reg_paint) {
if (!ps->root_tile_paint.pixmap)
get_root_tile(ps);
paint_region(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint,
ps->root_tile_paint.pict);
}
#endif
struct _xrender_win_data {
// Pixmap that the client window draws to,
// it will contain the content of client window.
xcb_pixmap_t pixmap;
// A Picture links to the Pixmap
xcb_render_picture_t pict;
// A buffer used for rendering
xcb_render_picture_t buffer;
// The rendered content of the window (dimmed, inverted
// color, etc.). This is either `buffer` or `pict`
xcb_render_picture_t rendered_pict;
xcb_pixmap_t shadow_pixmap;
xcb_render_picture_t shadow_pict;
};
static void compose(void *backend_data, session_t *ps, win *w, void *win_data, int dst_x,
int dst_y, const region_t *reg_paint) {
struct _xrender_data *xd = backend_data;
struct _xrender_win_data *wd = win_data;
bool blend = default_is_frame_transparent(NULL, w, win_data) ||
default_is_win_transparent(NULL, w, win_data);
int op = (blend ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
auto alpha_pict = xd->alpha_pict[(int)(((double)w->opacity / OPAQUE) * 255.0)];
// XXX Move shadow drawing into a separate function,
// also do shadow excluding outside of backend
// XXX This is needed to implement full-shadow
if (w->shadow) {
// Put shadow on background
region_t shadow_reg = win_extents_by_val(w);
region_t bshape = win_get_bounding_shape_global_by_val(w);
region_t reg_tmp;
pixman_region32_init(&reg_tmp);
// Shadow doesn't need to be painted underneath the body of the window
// Because no one can see it
pixman_region32_subtract(&reg_tmp, &shadow_reg, w->reg_ignore);
// Mask out the region we don't want shadow on
if (pixman_region32_not_empty(&ps->shadow_exclude_reg))
pixman_region32_subtract(&reg_tmp, &reg_tmp,
&ps->shadow_exclude_reg);
// Might be worth while to crop the region to shadow border
pixman_region32_intersect_rect(&reg_tmp, &reg_tmp, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, w->shadow_width,
w->shadow_height);
// Crop the shadow to the damage region. If we draw out side of
// the damage region, we could be drawing over perfectly good
// content, and destroying it.
pixman_region32_intersect(&reg_tmp, &reg_tmp, (region_t *)reg_paint);
#ifdef CONFIG_XINERAMA
if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 &&
w->xinerama_scr < ps->xinerama_nscrs)
// There can be a window where number of screens is updated,
// but the screen number attached to the windows have not.
//
// Window screen number will be updated eventually, so here we
// just check to make sure we don't access out of bounds.
pixman_region32_intersect(
&reg_tmp, &reg_tmp, &ps->xinerama_scr_regs[w->xinerama_scr]);
#endif
// Mask out the body of the window from the shadow
// Doing it here instead of in make_shadow() for saving GPU
// power and handling shaped windows (XXX unconfirmed)
pixman_region32_subtract(&reg_tmp, &reg_tmp, &bshape);
pixman_region32_fini(&bshape);
// Detect if the region is empty before painting
if (pixman_region32_not_empty(&reg_tmp)) {
x_set_picture_clip_region(ps, xd->target_buffer, 0, 0, &reg_tmp);
xcb_render_composite(
ps->c, XCB_RENDER_PICT_OP_OVER, wd->shadow_pict, alpha_pict,
xd->target_buffer, 0, 0, 0, 0, dst_x + w->shadow_dx,
dst_y + w->shadow_dy, w->shadow_width, w->shadow_height);
}
pixman_region32_fini(&reg_tmp);
pixman_region32_fini(&shadow_reg);
}
// Clip region of rendered_pict might be set during rendering, clear it to make
// sure we get everything into the buffer
x_clear_picture_clip_region(ps, wd->rendered_pict);
x_set_picture_clip_region(ps, xd->target_buffer, 0, 0, reg_paint);
xcb_render_composite(ps->c, op, wd->rendered_pict, alpha_pict, xd->target_buffer,
0, 0, 0, 0, dst_x, dst_y, w->widthb, w->heightb);
}
/**
* Reset filter on a <code>Picture</code>.
*/
static inline void xrfilter_reset(session_t *ps, xcb_render_picture_t p) {
const char *filter = "Nearest";
xcb_render_set_picture_filter(ps->c, p, strlen(filter), filter, 0, NULL);
}
static bool
blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_paint) {
struct _xrender_data *xd = backend_data;
const pixman_box32_t *reg = pixman_region32_extents((region_t *)reg_paint);
const int height = reg->y2 - reg->y1;
const int width = reg->x2 - reg->x1;
// Create a buffer for storing blurred picture, make it just big enough
// for the blur region
xcb_render_picture_t tmp_picture[2] = {
x_create_picture_with_visual(ps, width, height, ps->vis, 0, NULL),
x_create_picture_with_visual(ps, width, height, ps->vis, 0, NULL)};
region_t clip;
pixman_region32_init(&clip);
pixman_region32_copy(&clip, (region_t *)reg_paint);
pixman_region32_translate(&clip, -reg->x1, -reg->y1);
if (!tmp_picture[0] || !tmp_picture[1]) {
log_error("Failed to build intermediate Picture.");
return false;
}
x_set_picture_clip_region(ps, tmp_picture[0], 0, 0, &clip);
x_set_picture_clip_region(ps, tmp_picture[1], 0, 0, &clip);
// The multipass blur implemented here is not correct, but this is what old
// compton did anyway. XXX
xcb_render_picture_t src_pict = xd->target_buffer, dst_pict = tmp_picture[0];
auto alpha_pict = xd->alpha_pict[(int)(opacity * 255)];
int current = 0;
int src_x = reg->x1, src_y = reg->y1;
// For more than 1 pass, we do:
// target_buffer -(pass 1)-> tmp0 -(pass 2)-> tmp1 ...
// -(pass n-1)-> tmp0 or tmp1 -(pass n)-> target_buffer
// For 1 pass, we do
// target_buffer -(pass 1)-> tmp0 -(copy)-> target_buffer
int i;
for (i = 0; ps->o.blur_kerns[i]; i++) {
assert(i < MAX_BLUR_PASS - 1);
xcb_render_fixed_t *convolution_blur = ps->o.blur_kerns[i];
int kwid = XFIXED_TO_DOUBLE(convolution_blur[0]),
khei = XFIXED_TO_DOUBLE(convolution_blur[1]);
// Copy from source picture to destination. The filter must
// be applied on source picture, to get the nearby pixels outside the
// window.
xcb_render_set_picture_filter(
ps->c, src_pict, strlen(XRFILTER_CONVOLUTION), XRFILTER_CONVOLUTION,
kwid * khei + 2, convolution_blur);
if (ps->o.blur_kerns[i + 1] || i == 0) {
// This is not the last pass, or this is the first pass
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict,
XCB_NONE, dst_pict, src_x, src_y, 0, 0, 0, 0,
width, height);
} else {
// This is the last pass, and this is also not the first
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict,
alpha_pict, xd->target_buffer, 0, 0, 0, 0,
reg->x1, reg->y1, width, height);
}
xrfilter_reset(ps, src_pict);
src_pict = tmp_picture[current];
dst_pict = tmp_picture[!current];
src_x = 0;
src_y = 0;
current = !current;
}
// There is only 1 pass
if (i == 1) {
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict,
xd->target_buffer, 0, 0, 0, 0, reg->x1, reg->y1,
width, height);
}
xcb_render_free_picture(ps->c, tmp_picture[0]);
xcb_render_free_picture(ps->c, tmp_picture[1]);
return true;
}
static void render_win(void *backend_data, session_t *ps, win *w, void *win_data,
const region_t *reg_paint) {
struct _xrender_data *xd = backend_data;
struct _xrender_win_data *wd = win_data;
xcb_drawable_t draw = wd->pixmap;
if (!draw)
draw = w->id;
w->pixmap_damaged = false;
region_t reg_paint_local;
pixman_region32_init(&reg_paint_local);
pixman_region32_copy(&reg_paint_local, (region_t *)reg_paint);
pixman_region32_translate(&reg_paint_local, -w->g.x, -w->g.y);
if (!w->invert_color && w->frame_opacity == 1 && !w->dim) {
// No extra processing needed
wd->rendered_pict = wd->pict;
return;
}
// We don't want to modify the content of the original window when we process
// it, so we create a buffer.
if (wd->buffer == XCB_NONE) {
wd->buffer = x_create_picture_with_pictfmt(ps, w->widthb, w->heightb,
w->pictfmt, 0, NULL);
}
// Copy the content of the window over to the buffer
x_clear_picture_clip_region(ps, wd->buffer);
wd->rendered_pict = wd->buffer;
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, wd->pict, None,
wd->rendered_pict, 0, 0, 0, 0, 0, 0, w->widthb, w->heightb);
if (w->invert_color) {
// Handle invert color
x_set_picture_clip_region(ps, wd->rendered_pict, 0, 0, &reg_paint_local);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE,
xd->white_pixel, None, wd->rendered_pict, 0, 0, 0, 0,
0, 0, w->widthb, w->heightb);
// We use an extra PictOpInReverse operation to get correct pixel
// alpha. There could be a better solution.
if (win_has_alpha(w))
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_IN_REVERSE,
wd->pict, None, wd->rendered_pict, 0, 0, 0,
0, 0, 0, w->widthb, w->heightb);
}
const double dopacity = get_opacity_percent(w);
if (w->frame_opacity != 1) {
// Handle transparent frame
// Step 1: clip paint area to frame
region_t frame_reg;
pixman_region32_init(&frame_reg);
pixman_region32_copy(&frame_reg, &w->bounding_shape);
region_t body_reg = win_get_region_noframe_local_by_val(w);
pixman_region32_subtract(&frame_reg, &frame_reg, &body_reg);
// Draw the frame with frame opacity
xcb_render_picture_t alpha_pict =
xd->alpha_pict[(int)(w->frame_opacity * dopacity * 255)];
x_set_picture_clip_region(ps, wd->rendered_pict, 0, 0, &frame_reg);
// Step 2: multiply alpha value
// XXX test
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->white_pixel,
alpha_pict, wd->rendered_pict, 0, 0, 0, 0, 0, 0,
w->widthb, w->heightb);
}
if (w->dim) {
// Handle dimming
double dim_opacity = ps->o.inactive_dim;
if (!ps->o.inactive_dim_fixed)
dim_opacity *= get_opacity_percent(w);
xcb_render_color_t color = {
.red = 0, .green = 0, .blue = 0, .alpha = 0xffff * dim_opacity};
// Dim the actually content of window
xcb_rectangle_t rect = {
.x = 0,
.y = 0,
.width = w->widthb,
.height = w->heightb,
};
x_clear_picture_clip_region(ps, wd->rendered_pict);
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_OVER,
wd->rendered_pict, color, 1, &rect);
}
}
static void *prepare_win(void *backend_data, session_t *ps, win *w) {
auto wd = ccalloc(1, struct _xrender_win_data);
struct _xrender_data *xd = backend_data;
assert(w->a.map_state == XCB_MAP_STATE_VIEWABLE);
if (ps->has_name_pixmap) {
wd->pixmap = xcb_generate_id(ps->c);
xcb_composite_name_window_pixmap_checked(ps->c, w->id, wd->pixmap);
}
xcb_drawable_t draw = wd->pixmap;
if (!draw)
draw = w->id;
log_trace("%s %x", w->name, wd->pixmap);
wd->pict = x_create_picture_with_pictfmt_and_pixmap(ps, w->pictfmt, draw, 0, NULL);
wd->buffer = XCB_NONE;
// XXX delay allocating shadow pict until compose() will dramatical
// improve performance, probably because otherwise shadow pict
// can be created and destroyed multiple times per draw.
//
// However doing that breaks a assumption the backend API makes (i.e.
// either all needed data is here, or none is), therefore we will
// leave this here until we have chance to re-think the backend API
if (w->shadow) {
xcb_pixmap_t pixmap;
build_shadow(ps, 1, w->widthb, w->heightb, xd->shadow_pixel, &pixmap,
&wd->shadow_pict);
xcb_free_pixmap(ps->c, pixmap);
}
return wd;
}
static void release_win(void *backend_data, session_t *ps, win *w, void *win_data) {
struct _xrender_win_data *wd = win_data;
xcb_free_pixmap(ps->c, wd->pixmap);
// xcb_free_pixmap(ps->c, wd->shadow_pixmap);
xcb_render_free_picture(ps->c, wd->pict);
xcb_render_free_picture(ps->c, wd->shadow_pict);
if (wd->buffer != XCB_NONE)
xcb_render_free_picture(ps->c, wd->buffer);
free(wd);
}
static void *init(session_t *ps) {
auto xd = ccalloc(1, struct _xrender_data);
for (int i = 0; i < 256; ++i) {
double o = (double)i / 255.0;
xd->alpha_pict[i] = solid_picture(ps, false, o, 0, 0, 0);
assert(xd->alpha_pict[i] != None);
}
xd->black_pixel = solid_picture(ps, true, 1, 0, 0, 0);
xd->white_pixel = solid_picture(ps, true, 1, 1, 1, 1);
xd->shadow_pixel = solid_picture(ps, true, 1, ps->o.shadow_red,
ps->o.shadow_green, ps->o.shadow_blue);
if (ps->overlay != XCB_NONE) {
xd->target = x_create_picture_with_visual_and_pixmap(
ps, ps->vis, ps->overlay, 0, NULL);
} else {
xcb_render_create_picture_value_list_t pa = {
.subwindowmode = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS,
};
xd->target = x_create_picture_with_visual_and_pixmap(
ps, ps->vis, ps->root, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
}
xd->target_buffer = x_create_picture_with_visual(
ps, ps->root_width, ps->root_height, ps->vis, 0, NULL);
xcb_pixmap_t root_pixmap = x_get_root_back_pixmap(ps);
if (root_pixmap == XCB_NONE) {
xd->root_pict = solid_picture(ps, false, 1, 0.5, 0.5, 0.5);
} else {
xd->root_pict = x_create_picture_with_visual_and_pixmap(
ps, ps->vis, root_pixmap, 0, NULL);
}
return xd;
}
static void deinit(void *backend_data, session_t *ps) {
struct _xrender_data *xd = backend_data;
for (int i = 0; i < 256; i++)
xcb_render_free_picture(ps->c, xd->alpha_pict[i]);
xcb_render_free_picture(ps->c, xd->white_pixel);
xcb_render_free_picture(ps->c, xd->black_pixel);
free(xd);
}
static void *root_change(void *backend_data, session_t *ps) {
deinit(backend_data, ps);
return init(ps);
}
static void paint_root(void *backend_data, session_t *ps, const region_t *reg_paint) {
struct _xrender_data *xd = backend_data;
// Limit the paint area
x_set_picture_clip_region(ps, xd->target_buffer, 0, 0, reg_paint);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->root_pict, XCB_NONE,
xd->target_buffer, 0, 0, 0, 0, 0, 0, ps->root_width,
ps->root_height);
}
static void present(void *backend_data, session_t *ps) {
struct _xrender_data *xd = backend_data;
// compose() sets clip region, so clear it first to make
// sure we update the whole screen.
x_clear_picture_clip_region(ps, xd->target_buffer);
// TODO buffer-age-like optimization might be possible here.
// but that will require a different backend API
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->target_buffer, None,
xd->target, 0, 0, 0, 0, 0, 0, ps->root_width,
ps->root_height);
}
struct backend_info xrender_backend = {
.init = init,
.deinit = deinit,
.blur = blur,
.present = present,
.prepare = paint_root,
.compose = compose,
.root_change = root_change,
.render_win = render_win,
.prepare_win = prepare_win,
.release_win = release_win,
.is_win_transparent = default_is_win_transparent,
.is_frame_transparent = default_is_frame_transparent,
};

200
src/c2.c
View file

@ -30,6 +30,11 @@
#include "common.h"
#include "win.h"
#include "c2.h"
#include "string_utils.h"
#include "utils.h"
#include "log.h"
#pragma GCC diagnostic error "-Wunused-parameter"
#define C2_MAX_LEVELS 10
@ -218,8 +223,6 @@ static const c2_predef_t C2_PREDEFS[] = {
[C2_L_PROLE ] = { "role" , C2_L_TSTRING , 0 },
};
#define mstrncmp(s1, s2) strncmp((s1), (s2), strlen(s1))
/**
* Compare next word in a string with another string.
*/
@ -260,7 +263,7 @@ static inline c2_ptr_t
c2h_comb_tree(c2_b_op_t op, c2_ptr_t p1, c2_ptr_t p2) {
c2_ptr_t p = {
.isbranch = true,
.b = malloc(sizeof(c2_b_t))
.b = cmalloc(c2_b_t)
};
p.b->opr1 = p1;
@ -300,22 +303,19 @@ c2h_b_opcmp(c2_b_op_t op1, c2_b_op_t op2) {
}
static int
c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level);
c2_parse_grp(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);
c2_parse_target(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);
c2_parse_pattern(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);
c2_parse_legacy(const char *pattern, int offset, c2_ptr_t *presult);
static void
c2_free(c2_ptr_t p);
@ -337,18 +337,8 @@ 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 __attribute__((unused))
c2_dump(c2_ptr_t p) {
c2_dump_raw(p);
printf("\n");
fflush(stdout);
}
static void attr_unused
c2_dump(c2_ptr_t p);
static Atom
c2_get_atom_type(const c2_l_t *pleaf);
@ -360,7 +350,7 @@ c2_match_once(session_t *ps, win *w, const c2_ptr_t cond);
* Parse a condition string.
*/
c2_lptr_t *
c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern,
c2_parse(c2_lptr_t **pcondlst, const char *pattern,
void *data) {
if (!pattern)
return NULL;
@ -370,9 +360,9 @@ c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern,
int offset = -1;
if (strlen(pattern) >= 2 && ':' == pattern[1])
offset = c2_parse_legacy(ps, pattern, 0, &result);
offset = c2_parse_legacy(pattern, 0, &result);
else
offset = c2_parse_grp(ps, pattern, 0, &result, 0);
offset = c2_parse_grp(pattern, 0, &result, 0);
if (offset < 0) {
c2_freep(&result);
@ -382,10 +372,7 @@ c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern,
// Insert to pcondlst
{
static const 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.");
auto plptr = cmalloc(c2_lptr_t);
memcpy(plptr, &lptr_def, sizeof(c2_lptr_t));
plptr->ptr = result;
plptr->data = data;
@ -395,7 +382,7 @@ c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern,
}
#ifdef DEBUG_C2
printf_dbgf("(\"%s\"): ", pattern);
log_trace("(\"%s\"): ", pattern);
c2_dump(plptr->ptr);
#endif
@ -403,13 +390,12 @@ c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern,
}
}
#undef c2_error
#define c2_error(format, ...) do { \
printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \
## __VA_ARGS__); \
return -1; \
log_error("Pattern \"%s\" pos %d: " format, pattern, offset, ##__VA_ARGS__); \
goto fail; \
} while(0)
// TODO Not a very good macro
#define C2H_SKIP_SPACES() { while (isspace(pattern[offset])) ++offset; }
/**
@ -418,7 +404,7 @@ c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern,
* @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) {
c2_parse_grp(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.");
@ -429,13 +415,6 @@ c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult,
// 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
@ -525,24 +504,21 @@ c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult,
// 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;
if ((offset = c2_parse_grp(pattern, offset + 1, pele, level + 1)) < 0)
goto fail;
}
// Otherwise it's a leaf
else {
if ((offset = c2_parse_target(ps, pattern, offset, pele)) < 0)
goto c2_parse_grp_fail;
if ((offset = c2_parse_target(pattern, offset, pele)) < 0)
goto fail;
assert(!pele->isbranch && !c2_ptr_isempty(*pele));
if ((offset = c2_parse_op(pattern, offset, pele)) < 0)
goto c2_parse_grp_fail;
goto 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;
if ((offset = c2_parse_pattern(pattern, offset, pele)) < 0)
goto fail;
}
// Decrement offset -- we will increment it in loop update
--offset;
@ -588,30 +564,21 @@ c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult,
return offset;
c2_parse_grp_fail:
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) {
c2_parse_target(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.");
presult->l = cmalloc(c2_l_t);
c2_l_t * const pleaf = presult->l;
memcpy(pleaf, &leaf_def, sizeof(c2_l_t));
@ -631,7 +598,7 @@ c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presul
}
if (!tgtlen)
c2_error("Empty target.");
pleaf->tgt = mstrncpy(&pattern[offset - tgtlen], tgtlen);
pleaf->tgt = strndup(&pattern[offset - tgtlen], tgtlen);
// Check for predefined targets
for (unsigned i = 1; i < sizeof(C2_PREDEFS) / sizeof(C2_PREDEFS[0]); ++i) {
@ -656,7 +623,7 @@ c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presul
// Alias for custom properties
#define TGTFILL(target, type, format) \
(pleaf->target = mstrcpy(target), \
(pleaf->target = strdup(target), \
pleaf->type = type, \
pleaf->format = format)
@ -738,11 +705,11 @@ c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presul
if (type) {
if (pleaf->predef) {
printf_errf("(): Warning: Type specified for a default target will be ignored.");
log_warn("Type specified for a default target will be ignored.");
}
else {
if (pleaf->type && type != pleaf->type)
printf_errf("(): Warning: Default type overridden on target.");
log_warn("Default type overridden on target.");
pleaf->type = type;
}
}
@ -768,13 +735,12 @@ c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presul
// Write format
if (hasformat) {
if (pleaf->predef)
printf_errf("(): Warning: Format \"%d\" specified on a default target will be ignored.", format);
log_warn("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);
log_warn("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);
log_warn("Default format %d overridden on target.", pleaf->format);
pleaf->format = format;
}
}
@ -791,6 +757,9 @@ c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presul
c2_error("Invalid format.");
return offset;
fail:
return -1;
}
/**
@ -856,13 +825,16 @@ c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult) {
c2_error("Exists/greater-than/less-than operators cannot have a qualifier.");
return offset;
fail:
return -1;
}
/**
* 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_parse_pattern(const char *pattern, int offset, c2_ptr_t *presult) {
c2_l_t * const pleaf = presult->l;
// Exists operator cannot have pattern
@ -918,7 +890,7 @@ c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presu
// 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));
auto tptnstr = ccalloc((strlen(pattern + offset) + 1), char);
char *ptptnstr = tptnstr;
pleaf->ptnstr = tptnstr;
for (; pattern[offset] && delim != pattern[offset]; ++offset) {
@ -938,7 +910,7 @@ c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presu
case 'o':
case 'x':
{
char *tstr = mstrncpy(pattern + offset + 1, 2);
char *tstr = strndup(pattern + offset + 1, 2);
char *pstr = NULL;
long val = strtol(tstr, &pstr,
('o' == pattern[offset] ? 8: 16));
@ -961,7 +933,7 @@ c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presu
c2_error("Premature end of pattern string.");
++offset;
*ptptnstr = '\0';
pleaf->ptnstr = mstrcpy(tptnstr);
pleaf->ptnstr = strdup(tptnstr);
free(tptnstr);
}
@ -992,13 +964,16 @@ c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presu
c2_error("String pattern cannot have an arithmetic operator.");
return offset;
fail:
return -1;
}
/**
* Parse a condition with legacy syntax.
*/
static int
c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) {
c2_parse_legacy(const char *pattern, int offset, c2_ptr_t *presult) {
unsigned plen = strlen(pattern + offset);
if (plen < 4 || ':' != pattern[offset + 1]
@ -1006,9 +981,7 @@ c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presul
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.");
auto pleaf = cmalloc(c2_l_t);
presult->isbranch = false;
presult->l = pleaf;
memcpy(pleaf, &leaf_def, sizeof(c2_l_t));
@ -1054,18 +1027,15 @@ c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presul
++offset;
// Copy the pattern
pleaf->ptnstr = mstrcpy(pattern + offset);
if (!c2_l_postprocess(ps, pleaf))
return -1;
pleaf->ptnstr = strdup(pattern + offset);
return offset;
fail:
return -1;
}
#undef c2_error
#define c2_error(format, ...) { \
printf_err(format, ## __VA_ARGS__); \
return false; }
/**
* Do postprocessing on a condition leaf.
@ -1081,8 +1051,10 @@ c2_l_postprocess(session_t *ps, c2_l_t *pleaf) {
// 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);
if (!pleaf->tgtatom) {
log_error("Failed to get atom for target \"%s\".", pleaf->tgt);
return false;
}
}
// Insert target Atom into atom track list
@ -1096,9 +1068,7 @@ c2_l_postprocess(session_t *ps, c2_l_t *pleaf) {
}
}
if (!found) {
latom_t *pnew = malloc(sizeof(latom_t));
if (!pnew)
printf_errfq(1, "(): Failed to allocate memory for new track atom.");
auto pnew = cmalloc(latom_t);
pnew->next = ps->track_atom_lst;
pnew->atom = pleaf->tgtatom;
ps->track_atom_lst = pnew;
@ -1123,7 +1093,7 @@ c2_l_postprocess(session_t *ps, c2_l_t *pleaf) {
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);
log_warn("Lowercase character in target name \"%s\".", pleaf->tgt);
break;
}
}
@ -1143,9 +1113,11 @@ c2_l_postprocess(session_t *ps, c2_l_t *pleaf) {
// 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 "
if (!pleaf->regex_pcre) {
log_error("Pattern \"%s\": PCRE regular expression parsing failed on "
"offset %d: %s", pleaf->ptnstr, erroffset, error);
return false;
}
#ifdef CONFIG_REGEX_PCRE_JIT
pleaf->regex_pcre_extra = pcre_study(pleaf->regex_pcre,
PCRE_STUDY_JIT_COMPILE, &error);
@ -1159,12 +1131,32 @@ c2_l_postprocess(session_t *ps, c2_l_t *pleaf) {
// free(pleaf->tgt);
// pleaf->tgt = NULL;
#else
c2_error("PCRE regular expression support not compiled in.");
log_error("PCRE regular expression support not compiled in.");
return false;
#endif
}
return true;
}
static bool c2_tree_postprocess(session_t *ps, c2_ptr_t node) {
if (!node.isbranch) {
return c2_l_postprocess(ps, node.l);
}
if (!c2_tree_postprocess(ps, node.b->opr1))
return false;
return c2_tree_postprocess(ps, node.b->opr2);
}
bool c2_list_postprocess(session_t *ps, c2_lptr_t *list) {
c2_lptr_t *head = list;
while (head) {
if (!c2_tree_postprocess(ps, head->ptr))
return false;
head = head->next;
}
return true;
}
/**
* Free a condition tree.
*/
@ -1245,7 +1237,7 @@ c2h_dump_str_type(const c2_l_t *pleaf) {
* Dump a condition tree.
*/
static void
c2_dump_raw(c2_ptr_t p) {
c2_dump(c2_ptr_t p) {
// For a branch
if (p.isbranch) {
const c2_b_t * const pbranch = p.b;
@ -1257,7 +1249,7 @@ c2_dump_raw(c2_ptr_t p) {
putchar('!');
printf("(");
c2_dump_raw(pbranch->opr1);
c2_dump(pbranch->opr1);
switch (pbranch->op) {
case C2_B_OAND: printf(" && "); break;
@ -1266,7 +1258,7 @@ c2_dump_raw(c2_ptr_t p) {
default: assert(0); break;
}
c2_dump_raw(pbranch->opr2);
c2_dump(pbranch->opr2);
printf(")");
}
// For a leaf
@ -1482,7 +1474,7 @@ c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf,
int nstr;
if (wid_get_text_prop(ps, wid, pleaf->tgtatom, &strlst,
&nstr) && nstr > idx) {
tgt_free = mstrcpy(strlst[idx]);
tgt_free = strdup(strlst[idx]);
tgt = tgt_free;
}
if (strlst)
@ -1600,7 +1592,7 @@ c2_match_once(session_t *ps, win *w, const c2_ptr_t cond) {
}
#ifdef DEBUG_WINMATCH
printf_dbgf("(%#010lx): branch: result = %d, pattern = ", w->id, result);
log_trace("(%#010lx): branch: result = %d, pattern = ", w->id, result);
c2_dump(cond);
#endif
}
@ -1620,9 +1612,9 @@ c2_match_once(session_t *ps, win *w, const c2_ptr_t cond) {
}
#ifdef DEBUG_WINMATCH
printf_dbgf("(%#010lx): leaf: result = %d, error = %d, "
"client = %#010lx, pattern = ",
w->id, result, error, w->client_win);
log_trace("(%#010lx): leaf: result = %d, error = %d, "
"client = %#010lx, pattern = ",
w->id, result, error, w->client_win);
c2_dump(cond);
#endif
}

View file

@ -17,13 +17,11 @@ typedef struct _c2_lptr c2_lptr_t;
typedef struct session session_t;
typedef struct win win;
c2_lptr_t *
c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern,
void *data);
c2_lptr_t *c2_parse(c2_lptr_t **pcondlst, const char *pattern, void *data);
c2_lptr_t *
c2_free_lptr(c2_lptr_t *lp);
c2_lptr_t *c2_free_lptr(c2_lptr_t *lp);
bool
c2_match(session_t *ps, win *w, const c2_lptr_t *condlst,
const c2_lptr_t **cache, void **pdata);
bool c2_match(session_t *ps, win *w, const c2_lptr_t *condlst, const c2_lptr_t **cache,
void **pdata);
bool c2_list_postprocess(session_t *ps, c2_lptr_t *list);

View file

@ -16,18 +16,10 @@
// === 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
@ -58,9 +50,7 @@
#define COMPTON_VERSION "unknown"
#endif
#if defined(DEBUG_ALLOC_REG)
#define DEBUG_BACKTRACE 1
#endif
#define MAX_ALPHA (255)
// === Includes ===
@ -91,15 +81,8 @@
#include <ev.h>
#include <pixman.h>
// libdbus
#ifdef CONFIG_DBUS
#include <dbus/dbus.h>
#endif
#ifdef CONFIG_OPENGL
// libGL
#define GL_GLEXT_PROTOTYPES
#include <GL/glx.h>
// Workarounds for missing definitions in some broken GL drivers, thanks to
@ -119,36 +102,6 @@
#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
@ -163,6 +116,11 @@
#include "win.h"
#include "x.h"
#include "region.h"
#include "log.h"
#include "utils.h"
#include "compiler.h"
#include "kernel.h"
#include "options.h"
// === Constants ===
@ -196,9 +154,6 @@
/// @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
@ -214,11 +169,6 @@
// === Types ===
typedef long time_ms_t;
typedef struct _c2_lptr c2_lptr_t;
// Or use cmemzero().
/// Structure representing needed window updates.
typedef struct {
bool shadow : 1;
@ -227,20 +177,6 @@ typedef struct {
bool invert_color : 1;
} win_upd_t;
/// Structure representing Window property value.
typedef struct winprop {
union {
void *ptr;
uint8_t *p8;
int16_t *p16;
int32_t *p32;
uint32_t *c32; // 32bit cardinal
};
unsigned long nitems;
Atom type;
int format;
} winprop_t;
typedef struct _ignore {
struct _ignore *next;
unsigned long sequence;
@ -263,25 +199,6 @@ enum wincond_type {
#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,
@ -395,7 +312,7 @@ typedef struct {
GLint unifm_factor_center;
} glx_blur_pass_t;
typedef struct {
typedef struct glx_prog_main {
/// GLSL program.
GLuint prog;
/// Location of uniform "opacity" in window GLSL program.
@ -415,16 +332,11 @@ typedef struct {
#endif
#else
typedef uint32_t glx_prog_main_t;
struct glx_prog_main { };
#endif
#define PAINT_INIT { .pixmap = None, .pict = None }
typedef struct conv {
int size;
double *data;
} conv;
/// Linked list type of atoms.
typedef struct _latom {
Atom atom;
@ -433,205 +345,6 @@ typedef struct _latom {
#define REG_DATA_INIT { NULL, 0 }
typedef struct ev_session_timer ev_session_timer;
typedef struct ev_session_idle ev_session_idle;
typedef struct ev_session_prepare ev_session_prepare;
/// Structure representing all options.
typedef struct options_t {
// === Debugging ===
bool monitor_repaint;
bool print_diagnostics;
// === 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
/// <code>DISPLAY</code> 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 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;
/// Custom GLX program used for painting window.
glx_prog_main_t glx_prog_win;
/// Whether to fork to background.
bool fork_after_register;
/// Whether to detect rounded corners.
bool detect_rounded_corners;
/// 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 xcb_composite_name_window_pixmap(), 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 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;
/// argument string to shadow-exclude-reg option
char *shadow_exclude_reg_str;
/// 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.
xcb_render_fixed_t *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 <code>WM_TRANSIENT_FOR</code>.
bool detect_transient;
/// Whether to do window grouping with <code>WM_CLIENT_LEADER</code>.
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_OPENGL
/// Structure containing GLX-dependent data for a compton session.
typedef struct {
@ -689,8 +402,27 @@ typedef struct {
/// Structure containing all necessary data for a compton session.
typedef struct session {
// === Event handlers ===
/// ev_io for X connection
ev_io xiow;
/// Timeout for delayed unredirection.
ev_timer unredir_timer;
/// Timer for fading
ev_timer fade_timer;
/// Timer for delayed drawing, right now only used by
/// swopti
ev_timer delayed_draw_timer;
/// Use an ev_idle callback for drawing
/// So we only start drawing when events are processed
ev_idle draw_idle;
/// Called everytime we have timeouts or new data on socket,
/// so we can be sure if xcb read from X socket at anytime during event
/// handling, we will not left any event unhandled in the queue
ev_prepare event_check;
/// Signal handler for SIGUSR1
ev_signal usr1_signal;
/// backend data
void *backend_data;
/// libev mainloop
struct ev_loop *loop;
// === Display related ===
@ -735,25 +467,14 @@ typedef struct session {
#ifdef CONFIG_OPENGL
/// Pointer to GLX data.
glx_session_t *psglx;
/// Custom GLX program used for painting window.
// XXX should be in glx_session_t
glx_prog_main_t glx_prog_win;
#endif
// === Operation related ===
/// Program options.
options_t o;
/// Timeout for delayed unredirection.
ev_session_timer *unredir_timer;
/// Timer for fading
ev_session_timer *fade_timer;
/// Timer for delayed drawing, right now only used by
/// swopti
ev_session_timer *delayed_draw_timer;
/// Use an ev_idle callback for drawing
/// So we only start drawing when events are processed
ev_session_idle *draw_idle;
/// Called everytime we have timeouts or new data on socket,
/// so we can be sure if xcb read from X socket at anytime during event
/// handling, we will not left any event unhandled in the queue
ev_session_prepare *event_check;
/// Whether we have hit unredirection timeout.
bool tmout_unredir_hit;
/// Whether we need to redraw the screen
@ -772,7 +493,7 @@ typedef struct session {
/// Pre-generated alpha pictures.
xcb_render_picture_t *alpha_picts;
/// Time of last fading. In milliseconds.
time_ms_t fade_time;
unsigned long fade_time;
/// Head pointer of the error ignore linked list.
ignore_t *ignore_head;
/// Pointer to the <code>next</code> member of tail element of the error
@ -936,10 +657,7 @@ typedef struct session {
#ifdef CONFIG_DBUS
// === DBus related ===
// DBus connection.
DBusConnection *dbus_conn;
// DBus service name.
char *dbus_service;
void *dbus_data;
#endif
} session_t;
@ -959,8 +677,6 @@ typedef enum {
} 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 ==
@ -970,66 +686,8 @@ print_timestamp(session_t *ps);
void
ev_xcb_error(session_t *ps, xcb_generic_error_t *err);
#ifdef DEBUG_BACKTRACE
#include <execinfo.h>
#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);
}
#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.
*/
@ -1047,7 +705,7 @@ timeval_isempty(struct timeval *ptv) {
* @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) {
timeval_ms_cmp(struct timeval *ptv, unsigned long ms) {
assert(ptv);
// We use those if statement instead of a - expression because of possible
@ -1190,161 +848,6 @@ print_timestamp(session_t *ps) {
fprintf(stderr, "[ %5ld.%06ld ] ", diff.tv_sec, diff.tv_usec);
}
/**
* 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);
}
/**
* 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;
}
/**
* Wrapper of XFree() for convenience.
*
@ -1374,6 +877,7 @@ get_atom(session_t *ps, const char *atom_name) {
xcb_atom_t atom = XCB_NONE;
if (reply) {
log_debug("Atom %s is %d", atom_name, reply->atom);
atom = reply->atom;
free(reply);
} else
@ -1435,18 +939,6 @@ bkend_use_glx(session_t *ps) {
|| 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_OPENGL
return ps->psglx && ps->psglx->context;
#else
return false;
#endif
}
/**
* Check if a window is really focused.
*/
@ -1503,7 +995,7 @@ set_ignore(session_t *ps, unsigned long sequence) {
if (ps->o.show_all_xerrors)
return;
ignore_t *i = malloc(sizeof(ignore_t));
auto i = cmalloc(ignore_t);
if (!i) return;
i->sequence = sequence;
@ -1571,19 +1063,6 @@ wid_has_prop(const session_t *ps, Window w, Atom atom) {
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.
*/
@ -1609,21 +1088,6 @@ bool
wid_get_text_prop(session_t *ps, Window wid, Atom prop,
char ***pstrlst, int *pnstr);
/**
* Free a <code>winprop_t</code>.
*
* @param pprop pointer to the <code>winprop_t</code> to free.
*/
static inline void
free_winprop(winprop_t *pprop) {
// Empty the whole structure to avoid possible issues
if (pprop->ptr) {
cxfree(pprop->ptr);
pprop->ptr = NULL;
}
pprop->nitems = 0;
}
void
force_repaint(session_t *ps);
@ -1638,53 +1102,6 @@ vsync_deinit(session_t *ps);
*/
///@{
/**
* 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_OPENGL
if (*pfbo) {
glDeleteFramebuffers(1, pfbo);
*pfbo = 0;
}
#endif
assert(!*pfbo);
}
#ifdef CONFIG_OPENGL
/**
* 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
/**
@ -1696,8 +1113,8 @@ glx_mark_(session_t *ps, const char *func, XID xid, bool start) {
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));
auto str = ccalloc((strlen(func) + 12 + 2
+ strlen(postfix) + 5), char);
strcpy(str, func);
sprintf(str + strlen(str), "(%#010lx)%s", xid, postfix);
ps->psglx->glStringMarkerGREMEDY(strlen(str), str);
@ -1742,7 +1159,7 @@ xr_sync(session_t *ps, Drawable d, XSyncFence *pfence) {
if (!*pfence)
*pfence = XSyncCreateFence(ps->dpy, d, False);
if (*pfence) {
Bool __attribute__((unused)) triggered = False;
Bool attr_unused 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)
@ -1752,7 +1169,7 @@ xr_sync(session_t *ps, Drawable d, XSyncFence *pfence) {
assert(!XSyncQueryFence(ps->dpy, *pfence, &triggered) || triggered);
}
else {
printf_errf("(%#010lx): Failed to create X Sync fence.", d);
log_error("Failed to create X Sync fence for %#010lx", d);
}
free_fence(ps, &tmp_fence);
if (*pfence)
@ -1764,37 +1181,6 @@ xr_sync(session_t *ps, Drawable d, XSyncFence *pfence) {
*/
///@{
#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
*/
///@{
@ -1818,63 +1204,6 @@ opts_set_no_fading_openclose(session_t *ps, bool newval);
//!@}
#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);
}
/**
* Set a <code>bool</code> array of all wintypes to true.
*/

93
src/compiler.h Normal file
View file

@ -0,0 +1,93 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2018 Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <stdc-predef.h>
#define auto __auto_type
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#ifndef __has_attribute
# if __GNUC__ >= 4
# define __has_attribute(x) 1
# else
# define __has_attribute(x) 0
# endif
#endif
#if __has_attribute(const)
# define attr_const __attribute__((const))
#else
# define attr_const
#endif
#if __has_attribute(format)
# define attr_printf(a, b) __attribute__((format(printf, a, b)))
#else
# define attr_printf(a, b)
#endif
#if __has_attribute(pure)
# define attr_pure __attribute__((pure))
#else
# define attr_pure
#endif
#if __has_attribute(unused)
# define attr_unused __attribute__((unused))
#else
# define attr_unused
#endif
#if __has_attribute(warn_unused_result)
# define attr_warn_unused_result __attribute__((warn_unused_result))
#else
# define attr_warn_unused_result
#endif
#if __has_attribute(nonnull)
# define attr_nonnull(...) __attribute__((nonnull(__VA_ARGS__)))
# define attr_nonnull_all __attribute__((nonnull))
#else
# define attr_nonnull(...)
# define attr_nonnull_all
#endif
#if __has_attribute(returns_nonnull)
# define attr_ret_nonnull __attribute__((returns_nonnull))
#else
# define attr_ret_nonnull
#endif
#if __has_attribute(malloc)
# define attr_malloc __attribute__((malloc))
#else
# define attr_malloc
#endif
#if __STDC_VERSION__ >= 201112L
# define attr_noret _Noreturn
#else
# if __has_attribute(noreturn)
# define attr_noret __attribute__((noreturn))
# else
# define attr_noret
# endif
#endif
#if defined(__GNUC__) || defined(__clang__)
# define unreachable __builtin_unreachable()
#else
# define unreachable do {} while(0)
#endif
#ifndef __STDC_NO_THREADS__
# include <threads.h>
#elif __STDC_VERSION__ >= 201112L
# define thread_local _Thread_local
#elif defined(__GNUC__) || defined(__clang__)
# define thread_local __thread
#else
# define thread_local _Pragma("GCC error \"No thread local storage support\"") __error__
#endif

File diff suppressed because it is too large Load diff

View file

@ -33,6 +33,8 @@
#include "win.h"
#include "x.h"
#include "c2.h"
#include "log.h" // XXX clean up
#include "render.h"
// == Functions ==
// TODO move static inline functions that are only used in compton.c, into
@ -52,22 +54,12 @@ win *find_toplevel2(session_t *ps, Window wid);
void map_win(session_t *ps, Window id);
/**
* Reset filter on a <code>Picture</code>.
*/
static inline void
xrfilter_reset(session_t *ps, xcb_render_picture_t p) {
#define FILTER "Nearest"
xcb_render_set_picture_filter(ps->c, p, strlen(FILTER), FILTER, 0, NULL);
#undef FILTER
}
/**
* Subtract two unsigned long values.
*
* Truncate to 0 if the result is negative.
*/
static inline unsigned long __attribute__((const))
static inline unsigned long attr_const
sub_unslong(unsigned long a, unsigned long b) {
return (a > b) ? a - b : 0;
}
@ -102,17 +94,6 @@ array_wid_exists(const Window *arr, int count, Window wid) {
return false;
}
/**
* Destroy a <code>Picture</code>.
*/
inline static void
free_picture(session_t *ps, xcb_render_picture_t *p) {
if (*p) {
xcb_render_free_picture(ps->c, *p);
*p = None;
}
}
/**
* Destroy a condition list.
*/
@ -122,51 +103,13 @@ free_wincondlst(c2_lptr_t **pcondlst) {
continue;
}
#ifdef CONFIG_OPENGL
/**
* Bind texture in paint_t if we are using GLX backend.
*/
static inline bool
paint_bind_tex(session_t *ps, paint_t *ppaint,
unsigned wid, unsigned hei, unsigned depth, bool force)
{
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);
return true;
}
#else
static inline bool
paint_bind_tex(session_t *ps, paint_t *ppaint,
unsigned wid, unsigned hei, unsigned depth, bool force)
{
return true;
}
#ifndef CONFIG_OPENGL
static inline void
free_paint_glx(session_t *ps, paint_t *p) {}
static inline void
free_win_res_glx(session_t *ps, win *w) {}
static inline void
free_texture(session_t *ps, glx_texture_t **t) {
assert(!*t);
}
#endif
/**
* Free paint_t.
*/
static inline void
free_paint(session_t *ps, paint_t *ppaint) {
free_paint_glx(ps, ppaint);
free_picture(ps, &ppaint->pict);
if (ppaint->pixmap)
xcb_free_pixmap(ps->c, ppaint->pixmap);
ppaint->pixmap = XCB_NONE;
}
/**
* Create a XTextProperty of a single string.
*/
@ -191,7 +134,7 @@ 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);
log_error("Failed to make text property: %s.", str);
return false;
}
@ -212,66 +155,22 @@ dump_drawable(session_t *ps, Drawable drawable) {
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);
log_trace("Drawable %#010lx: x = %u, y = %u, wid = %u, hei = %d, b = %u, d = %u",
drawable, x, y, width, height, border, depth);
}
else {
printf_dbgf("(%#010lx): Failed\n", drawable);
log_trace("Drawable %#010lx: Failed", drawable);
}
}
/**
* 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, xcb_pixmap_t 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))
if (!x_validate_pixmap(ps, w->paint.pixmap))
free_paint(ps, &w->paint);
}
/**
* Normalize a convolution kernel.
*/
static inline void
normalize_conv_kern(int wid, int hei, xcb_render_fixed_t *kern) {
double sum = 0.0;
for (int i = 0; i < wid * hei; ++i)
sum += XFIXED_TO_DOUBLE(kern[i]);
double factor = 1.0 / sum;
for (int i = 0; i < wid * hei; ++i)
kern[i] = DOUBLE_TO_XFIXED(XFIXED_TO_DOUBLE(kern[i]) * factor);
}
#ifdef CONFIG_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
// vim: set et sw=2 :

View file

@ -5,10 +5,14 @@
#include <stdlib.h>
#include <stdbool.h>
#include "compiler.h"
#include "common.h"
#include "config.h"
#include "utils.h"
#include "c2.h"
#include "string_utils.h"
#include "log.h"
#include "config.h"
/**
* Parse a long number.
@ -18,13 +22,13 @@ 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);
log_error("Invalid number: %s", s);
return false;
}
while (isspace(*endptr))
++endptr;
if (*endptr) {
printf_errf("(\"%s\"): Trailing characters.", s);
log_error("Trailing characters: %s", s);
return false;
}
*dest = val;
@ -39,7 +43,7 @@ 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);
log_error("No number found: %s", src);
return src;
}
@ -55,48 +59,44 @@ parse_matrix_readnum(const char *src, double *dest) {
* Parse a matrix.
*/
xcb_render_fixed_t *
parse_matrix(session_t *ps, const char *src, const char **endptr) {
parse_matrix(const char *src, const char **endptr, bool *hasneg) {
int wid = 0, hei = 0;
*hasneg = false;
const char *pc = NULL;
xcb_render_fixed_t *matrix = NULL;
// Get matrix width and height
{
double val = 0.0;
if (src == (pc = parse_matrix_readnum(src, &val)))
goto parse_matrix_err;
goto err1;
src = pc;
wid = val;
if (src == (pc = parse_matrix_readnum(src, &val)))
goto parse_matrix_err;
goto err1;
src = pc;
hei = val;
}
// Validate matrix width and height
if (wid <= 0 || hei <= 0) {
printf_errf("(): Invalid matrix width/height.");
goto parse_matrix_err;
log_error("Invalid matrix width/height.");
goto err1;
}
if (!(wid % 2 && hei % 2)) {
printf_errf("(): Width/height not odd.");
goto parse_matrix_err;
log_error("Width/height not odd.");
goto err1;
}
if (wid > 16 || hei > 16)
printf_errf("(): Matrix width/height too large, may slow down"
"rendering, and/or consume lots of memory");
log_warn("Matrix width/height too large, may slow down"
"rendering, and/or consume lots of memory");
// Allocate memory
matrix = calloc(wid * hei + 2, sizeof(xcb_render_fixed_t));
if (!matrix) {
printf_errf("(): Failed to allocate memory for matrix.");
goto parse_matrix_err;
}
auto matrix = ccalloc(wid * hei + 2, xcb_render_fixed_t);
// 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) {
@ -105,21 +105,18 @@ parse_matrix(session_t *ps, const char *src, const char **endptr) {
}
double val = 0;
if (src == (pc = parse_matrix_readnum(src, &val)))
goto parse_matrix_err;
goto err2;
src = pc;
if (val < 0) hasneg = true;
if (val < 0) *hasneg = true;
matrix[2 + i] = DOUBLE_TO_XFIXED(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;
log_error("Trailing characters in matrix string.");
goto err2;
}
// Jump over spaces after ';'
@ -134,8 +131,8 @@ parse_matrix(session_t *ps, const char *src, const char **endptr) {
if (endptr)
*endptr = pc;
else if (*pc) {
printf_errf("(): Only one matrix expected.");
goto parse_matrix_err;
log_error("Only one matrix expected.");
goto err2;
}
// Fill in width and height
@ -144,24 +141,31 @@ parse_matrix(session_t *ps, const char *src, const char **endptr) {
return matrix;
parse_matrix_err:
err2:
free(matrix);
err1:
return NULL;
}
/**
* Parse a convolution kernel.
*
* Output:
* hasneg: whether the convolution kernel has negative values
*/
xcb_render_fixed_t *
parse_conv_kern(session_t *ps, const char *src, const char **endptr) {
return parse_matrix(ps, src, endptr);
parse_conv_kern(const char *src, const char **endptr, bool *hasneg) {
return parse_matrix(src, endptr, hasneg);
}
/**
* Parse a list of convolution kernels.
*
* Output:
* hasneg: whether any of the convolution kernel has negative values
*/
bool
parse_conv_kern_lst(session_t *ps, const char *src, xcb_render_fixed_t **dest, int max) {
parse_conv_kern_lst(const char *src, xcb_render_fixed_t **dest, int max, bool *hasneg) {
static const struct {
const char *name;
const char *kern_str;
@ -175,10 +179,13 @@ parse_conv_kern_lst(session_t *ps, const char *src, xcb_render_fixed_t **dest, i
{ "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," },
};
*hasneg = false;
for (unsigned 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);
return parse_conv_kern_lst(CONV_KERN_PREDEF[i].kern_str, dest, max, hasneg);
int i = 0;
const char *pc = src;
@ -192,18 +199,20 @@ parse_conv_kern_lst(session_t *ps, const char *src, xcb_render_fixed_t **dest, i
// 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)))
bool tmp_hasneg;
if (!(dest[i++] = parse_conv_kern(pc, &pc, &tmp_hasneg)))
return false;
*hasneg |= tmp_hasneg;
}
if (i > 1) {
printf_errf("(): You are seeing this message because your are using multipass\n"
"blur. Please report an issue to us so we know multipass blur is actually been used.\n"
"Otherwise it might be removed in future releases");
log_warn("You are seeing this message because your are using multipassblur. Please "
"report an issue to us so we know multipass blur is actually been used. "
"Otherwise it might be removed in future releases");
}
if (*pc) {
printf_errf("(): Too many blur kernels!");
log_error("Too many blur kernels!");
return false;
}
@ -239,7 +248,7 @@ parse_geometry(session_t *ps, const char *src, region_t *dest) {
if (src != endptr) {
geom.wid = val;
if (geom.wid < 0) {
printf_errf("(\"%s\"): Invalid width.", src);
log_error("Invalid width: %s", src);
return false;
}
src = endptr;
@ -255,7 +264,7 @@ parse_geometry(session_t *ps, const char *src, region_t *dest) {
if (src != endptr) {
geom.hei = val;
if (geom.hei < 0) {
printf_errf("(\"%s\"): Invalid height.", src);
log_error("Invalid height: %s", src);
return false;
}
src = endptr;
@ -288,7 +297,7 @@ parse_geometry(session_t *ps, const char *src, region_t *dest) {
}
if (*src) {
printf_errf("(\"%s\"): Trailing characters.", src);
log_error("Trailing characters: %s", src);
return false;
}
@ -300,16 +309,16 @@ parse_geometry_end:
/**
* Parse a list of opacity rules.
*/
bool parse_rule_opacity(session_t *ps, const char *src) {
bool parse_rule_opacity(c2_lptr_t **res, const char *src) {
// Find opacity value
char *endptr = NULL;
long val = strtol(src, &endptr, 0);
if (!endptr || endptr == src) {
printf_errf("(\"%s\"): No opacity specified?", src);
log_error("No opacity specified: %s", src);
return false;
}
if (val > 100 || val < 0) {
printf_errf("(\"%s\"): Opacity %ld invalid.", src, val);
log_error("Opacity %ld invalid: %s", val, src);
return false;
}
@ -317,26 +326,78 @@ bool parse_rule_opacity(session_t *ps, const char *src) {
while (*endptr && isspace(*endptr))
++endptr;
if (':' != *endptr) {
printf_errf("(\"%s\"): Opacity terminator not found.", src);
log_error("Opacity terminator not found: %s", src);
return false;
}
++endptr;
// Parse pattern
// I hope 1-100 is acceptable for (void *)
return c2_parse(ps, &ps->o.opacity_rules, endptr, (void *) val);
return c2_parse(res, endptr, (void *) val);
}
/**
* Add a pattern to a condition linked list.
*/
bool
condlst_add(session_t *ps, c2_lptr_t **pcondlst, const char *pattern) {
condlst_add(c2_lptr_t **pcondlst, const char *pattern) {
if (!pattern)
return false;
if (!c2_parse(ps, pcondlst, pattern, NULL))
if (!c2_parse(pcondlst, pattern, NULL))
exit(1);
return true;
}
char *parse_config(options_t *opt, const char *config_file,
bool *shadow_enable, bool *fading_enable, bool *hasneg,
win_option_mask_t *winopt_mask) {
char *ret = NULL;
#ifdef CONFIG_LIBCONFIG
ret = parse_config_libconfig(opt, config_file, shadow_enable, fading_enable,
hasneg, winopt_mask);
#endif
// Apply default wintype options that does not depends on global options.
// For example, wintype shadow option will depend on the global shadow
// option, so it is not set here.
//
// Except desktop windows are always drawn without shadow.
if (!winopt_mask[WINTYPE_DESKTOP].shadow) {
winopt_mask[WINTYPE_DESKTOP].shadow = true;
opt->wintype_option[WINTYPE_DESKTOP].shadow = false;
}
// Focused/unfocused state only apply to a few window types, all other windows
// are always considered focused.
const wintype_t nofocus_type[] =
{ WINTYPE_UNKNOWN, WINTYPE_NORMAL, WINTYPE_UTILITY };
for (unsigned long i = 0; i < ARR_SIZE(nofocus_type); i++) {
if (!winopt_mask[nofocus_type[i]].focus) {
winopt_mask[nofocus_type[i]].focus = true;
opt->wintype_option[nofocus_type[i]].focus = false;
}
}
for (unsigned long i = 0; i < NUM_WINTYPES; i++) {
if (!winopt_mask[i].focus) {
winopt_mask[i].focus = true;
opt->wintype_option[i].focus = true;
}
if (!winopt_mask[i].full_shadow) {
winopt_mask[i].full_shadow = true;
opt->wintype_option[i].full_shadow = false;
}
if (!winopt_mask[i].redir_ignore) {
winopt_mask[i].redir_ignore = true;
opt->wintype_option[i].redir_ignore = false;
}
if (!winopt_mask[i].opacity) {
winopt_mask[i].opacity = true;
// Opacity is not set to a concrete number here because the opacity logic
// is complicated, and needs an "unset" state
opt->wintype_option[i].opacity = NAN;
}
}
return ret;
}

View file

@ -1,7 +1,11 @@
#pragma once
// SPDX-License-Identifier: MIT
// Copyright (c) 2011-2013, Christopher Jeffrey
// Copyright (c) 2013 Richard Grenville <pyxlcy@gmail.com>
// Copyright (c) 2018 Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
/// Common functions and definitions for configuration parsing
/// Used for command line arguments and config files
#include <stdbool.h>
@ -11,32 +15,348 @@
#include "common.h"
bool parse_long(const char *, long *);
const char *parse_matrix_readnum(const char *, double *);
xcb_render_fixed_t *parse_matrix(session_t *, const char *, const char **);
xcb_render_fixed_t *parse_conv_kern(session_t *, const char *, const char **);
bool parse_conv_kern_lst(session_t *, const char *, xcb_render_fixed_t **, int);
bool parse_geometry(session_t *, const char *, region_t *);
bool parse_rule_opacity(session_t *, const char *);
/// 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,
};
typedef struct win_option_mask {
bool shadow : 1;
bool fade : 1;
bool focus : 1;
bool full_shadow : 1;
bool redir_ignore : 1;
bool opacity : 1;
} win_option_mask_t;
typedef struct win_option {
bool shadow;
bool fade;
bool focus;
bool full_shadow;
bool redir_ignore;
double opacity;
} win_option_t;
typedef struct _c2_lptr c2_lptr_t;
// This macro is here because this is the maximum number
// of blur passes options_t can hold, not a limitation of
// rendering.
/// @brief Maximum passes for blur.
#define MAX_BLUR_PASS 5
/// Structure representing all options.
typedef struct options_t {
// === Debugging ===
bool monitor_repaint;
bool print_diagnostics;
// === General ===
/// The configuration file we used.
char *config_file;
/// Path to write PID to.
char *write_pid_path;
/// 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 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;
/// Whether to fork to background.
bool fork_after_register;
/// Whether to detect rounded corners.
bool detect_rounded_corners;
/// 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, in milliseconds.
unsigned long 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 show all X errors.
bool show_all_xerrors;
/// Whether to avoid acquiring X Selection.
bool no_x_selection;
/// Window type option override.
win_option_t wintype_option[NUM_WINTYPES];
// === 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 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 ===
/// 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;
/// argument string to shadow-exclude-reg option
char *shadow_exclude_reg_str;
/// 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 ===
/// 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.
unsigned long 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 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;
// === 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.
xcb_render_fixed_t *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 ===
/// 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 <code>WM_TRANSIENT_FOR</code>.
bool detect_transient;
/// Whether to do window grouping with <code>WM_CLIENT_LEADER</code>.
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;
extern const char *const VSYNC_STRS[NUM_VSYNC + 1];
extern const char *const BACKEND_STRS[NUM_BKEND + 1];
attr_warn_unused_result bool parse_long(const char *, long *);
attr_warn_unused_result const char *parse_matrix_readnum(const char *, double *);
attr_warn_unused_result xcb_render_fixed_t *
parse_matrix(const char *, const char **, bool *hasneg);
attr_warn_unused_result xcb_render_fixed_t *
parse_conv_kern(const char *, const char **, bool *hasneg);
attr_warn_unused_result bool
parse_conv_kern_lst(const char *, xcb_render_fixed_t **, int, bool *hasneg);
attr_warn_unused_result bool parse_geometry(session_t *, const char *, region_t *);
attr_warn_unused_result bool parse_rule_opacity(c2_lptr_t **, const char *);
/**
* Add a pattern to a condition linked list.
*/
bool condlst_add(session_t *, c2_lptr_t **, const char *);
bool condlst_add(c2_lptr_t **, const char *);
#ifdef CONFIG_LIBCONFIG
FILE *
open_config_file(char *cpath, char **path);
void
parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst,
const char *name);
void
parse_config(session_t *ps, struct options_tmp *pcfgtmp);
#else
static inline void parse_config(session_t *a, struct options_tmp *b) {
(void)a;
(void)b;
}
/// Parse a configuration file
/// Returns the actually config_file name used, allocated on heap
/// Outputs:
/// shadow_enable = whether shaodw is enabled globally
/// fading_enable = whether fading is enabled globally
/// win_option_mask = whether option overrides for specific window type is set for given
/// options
/// hasneg = whether the convolution kernel has negative values
char *
parse_config_libconfig(options_t *, const char *config_file, bool *shadow_enable,
bool *fading_enable, bool *hasneg, win_option_mask_t *winopt_mask);
#endif
/// Parse a configuration file is that is enabled, also initialize the winopt_mask with
/// default values
/// Outputs and returns:
/// same as parse_config_libconfig
char *parse_config(options_t *, const char *config_file, bool *shadow_enable,
bool *fading_enable, bool *hasneg, win_option_mask_t *winopt_mask);
/**
* Parse a backend option argument.
*/
static inline attr_const enum backend parse_backend(const char *str) {
for (enum backend i = 0; BACKEND_STRS[i]; ++i) {
if (!strcasecmp(str, BACKEND_STRS[i])) {
return i;
}
}
// Keep compatibility with an old revision containing a spelling mistake...
if (!strcasecmp(str, "xr_glx_hybird")) {
log_warn("backend xr_glx_hybird should be xr_glx_hybrid, the misspelt"
"version will be removed soon.");
return BKEND_XR_GLX_HYBRID;
}
// cju wants to use dashes
if (!strcasecmp(str, "xr-glx-hybrid")) {
log_warn("backend xr-glx-hybrid should be xr_glx_hybrid, the alternative"
"version will be removed soon.");
return BKEND_XR_GLX_HYBRID;
}
log_error("Invalid backend argument: %s", str);
return NUM_BKEND;
}
/**
* Parse a glx_swap_method option argument.
*
* Returns -2 on failure
*/
static inline attr_const int parse_glx_swap_method(const char *str) {
// Parse alias
if (!strcmp("undefined", str)) {
return 0;
}
if (!strcmp("copy", str)) {
return 1;
}
if (!strcmp("exchange", str)) {
return 2;
}
if (!strcmp("buffer-age", str)) {
return -1;
}
// Parse number
char *pc = NULL;
int age = strtol(str, &pc, 0);
if (!pc || str == pc) {
log_error("glx-swap-method is an invalid number: %s", str);
return -2;
}
for (; *pc; ++pc)
if (!isspace(*pc)) {
log_error("Trailing characters in glx-swap-method option: %s", str);
return -2;
}
if (age < -1) {
log_error("Number for glx-swap-method is too small: %s", str);
return -2;
}
return age;
}
/**
* Parse a VSync option argument.
*/
static inline vsync_t parse_vsync(const char *str) {
for (vsync_t i = 0; VSYNC_STRS[i]; ++i)
if (!strcasecmp(str, VSYNC_STRS[i])) {
return i;
}
log_error("Invalid vsync argument: %s", str);
return NUM_VSYNC;
}
// vim: set noet sw=8 ts=8 :

View file

@ -7,9 +7,15 @@
#include <libgen.h>
#include <libconfig.h>
#include <basedir_fs.h>
#include "common.h"
#include "config.h"
#include "string_utils.h"
#include "options.h"
#include "log.h"
#pragma GCC diagnostic error "-Wunused-parameter"
/**
* Wrapper of libconfig's <code>config_lookup_int</code>.
@ -33,80 +39,45 @@ lcfg_lookup_bool(const config_t *config, const char *path, bool *value) {
* Follows the XDG specification to search for the configuration file.
*/
FILE *
open_config_file(char *cpath, char **ppath) {
static const char *config_filename = "/compton.conf";
static const char *config_filename_legacy = "/.compton.conf";
static const char *config_home_suffix = "/.config";
static const char *config_system_dir = "/etc/xdg";
open_config_file(const char *cpath, char **ppath) {
static const char *config_paths[] = {
"/compton.conf",
"/compton/compton.conf"
};
static const char config_filename_legacy[] = "/.compton.conf";
char *dir = NULL, *home = NULL;
char *path = cpath;
FILE *f = NULL;
if (path) {
f = fopen(path, "r");
if (f && ppath)
*ppath = path;
return f;
if (cpath) {
FILE *ret = fopen(cpath, "r");
if (ret && ppath)
*ppath = strdup(cpath);
return ret;
}
// 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
for (size_t i = 0; i < ARR_SIZE(config_paths); i++) {
char *path = xdgConfigFind(config_paths[i], NULL);
FILE *ret = fopen(path, "r");
if (ret && ppath) {
*ppath = strdup(path);
}
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, ":");
if (ret) {
return ret;
}
}
else {
path = mstrjoin(config_system_dir, config_filename);
f = fopen(path, "r");
if (f && ppath)
// Fall back to legacy config file names
const char *home = getenv("HOME");
if (home && strlen(home)) {
auto path = ccalloc(strlen(home)+strlen(config_filename_legacy)+1, char);
strcpy(path, home);
strcpy(path+strlen(home), config_filename_legacy);
FILE *ret = fopen(path, "r");
if (ret && ppath)
*ppath = path;
else
free(path);
if (f)
return f;
return ret;
}
return NULL;
}
@ -114,7 +85,7 @@ open_config_file(char *cpath, char **ppath) {
* Parse a condition list in configuration file.
*/
void
parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst,
parse_cfg_condlst(const config_t *pcfg, c2_lptr_t **pcondlst,
const char *name) {
config_setting_t *setting = config_lookup(pcfg, name);
if (setting) {
@ -122,11 +93,11 @@ parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst,
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));
condlst_add(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));
condlst_add(pcondlst, config_setting_get_string(setting));
}
}
}
@ -135,29 +106,33 @@ parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst,
* 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) {
parse_cfg_condlst_opct(options_t *opt, 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)))
if (!parse_rule_opacity(&opt->opacity_rules,
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));
else if (config_setting_type(setting) == CONFIG_TYPE_STRING) {
if (!parse_rule_opacity(&opt->opacity_rules, config_setting_get_string(setting)))
exit(1);
}
}
}
/**
* Parse a configuration file from default location.
*
* Returns the actually config_file name
*/
void
parse_config(session_t *ps, struct options_tmp *pcfgtmp) {
char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shadow_enable,
bool *fading_enable, bool *conv_kern_hasneg, win_option_mask_t *winopt_mask)
{
char *path = NULL;
FILE *f;
config_t cfg;
@ -168,22 +143,21 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) {
// anything
const char *sval = NULL;
f = open_config_file(ps->o.config_file, &path);
f = open_config_file(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;
if (config_file) {
log_fatal("Failed to read configuration file \"%s\".", config_file);
abort();
}
return;
free(path);
return NULL;
}
config_init(&cfg);
{
// dirname() could modify the original string, thus we must pass a
// copy
char *path2 = mstrcpy(path);
char *path2 = strdup(path);
char *parent = dirname(path2);
if (parent)
@ -196,201 +170,268 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) {
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));
if (read_result == CONFIG_FALSE) {
log_error("Error when reading configuration file \"%s\", line %d: %s",
path, config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
free(path);
return;
return NULL;
}
}
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 (config_lookup_int(&cfg, "fade-delta", &ival))
ps->o.fade_delta = ival;
opt->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;
opt->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;
opt->fade_out_step = normalize_d(dval) * OPAQUE;
// -r (shadow_radius)
config_lookup_int(&cfg, "shadow-radius", &ps->o.shadow_radius);
config_lookup_int(&cfg, "shadow-radius", &opt->shadow_radius);
// -o (shadow_opacity)
config_lookup_float(&cfg, "shadow-opacity", &ps->o.shadow_opacity);
config_lookup_float(&cfg, "shadow-opacity", &opt->shadow_opacity);
// -l (shadow_offset_x)
config_lookup_int(&cfg, "shadow-offset-x", &ps->o.shadow_offset_x);
config_lookup_int(&cfg, "shadow-offset-x", &opt->shadow_offset_x);
// -t (shadow_offset_y)
config_lookup_int(&cfg, "shadow-offset-y", &ps->o.shadow_offset_y);
config_lookup_int(&cfg, "shadow-offset-y", &opt->shadow_offset_y);
// -i (inactive_opacity)
if (config_lookup_float(&cfg, "inactive-opacity", &dval))
ps->o.inactive_opacity = normalize_d(dval) * OPAQUE;
opt->inactive_opacity = normalize_d(dval) * OPAQUE;
// --active_opacity
if (config_lookup_float(&cfg, "active-opacity", &dval))
ps->o.active_opacity = normalize_d(dval) * OPAQUE;
opt->active_opacity = normalize_d(dval) * OPAQUE;
// -e (frame_opacity)
config_lookup_float(&cfg, "frame-opacity", &ps->o.frame_opacity);
config_lookup_float(&cfg, "frame-opacity", &opt->frame_opacity);
// -c (shadow_enable)
if (config_lookup_bool(&cfg, "shadow", &ival) && ival)
wintype_arr_enable(ps->o.wintype_shadow);
if (config_lookup_bool(&cfg, "shadow", &ival))
*shadow_enable = ival;
// -C (no_dock_shadow)
lcfg_lookup_bool(&cfg, "no-dock-shadow", &pcfgtmp->no_dock_shadow);
if (config_lookup_bool(&cfg, "no-dock-shadow", &ival)) {
log_warn("Option `no-dock-shadow` is deprecated, and will be removed."
" Please use the wintype option `shadow` of `dock` instead.");
opt->wintype_option[WINTYPE_DOCK].shadow = false;
winopt_mask[WINTYPE_DOCK].shadow = true;
}
// -G (no_dnd_shadow)
lcfg_lookup_bool(&cfg, "no-dnd-shadow", &pcfgtmp->no_dnd_shadow);
if (config_lookup_bool(&cfg, "no-dnd-shadow", &ival)) {
log_warn("Option `no-dnd-shadow` is deprecated, and will be removed."
" Please use the wintype option `shadow` of `dnd` instead.");
opt->wintype_option[WINTYPE_DND].shadow = false;
winopt_mask[WINTYPE_DND].shadow = true;
};
// -m (menu_opacity)
config_lookup_float(&cfg, "menu-opacity", &pcfgtmp->menu_opacity);
if (config_lookup_float(&cfg, "menu-opacity", &dval)) {
log_warn("Option `menu-opacity` is deprecated, and will be removed.Please use the "
"wintype option `opacity` of `popup_menu` and `dropdown_menu` instead.");
opt->wintype_option[WINTYPE_DROPDOWN_MENU].opacity = dval;
opt->wintype_option[WINTYPE_POPUP_MENU].opacity = dval;
winopt_mask[WINTYPE_DROPDOWN_MENU].opacity = true;
winopt_mask[WINTYPE_POPUP_MENU].opacity = true;
}
// -f (fading_enable)
if (config_lookup_bool(&cfg, "fading", &ival) && ival)
wintype_arr_enable(ps->o.wintype_fade);
if (config_lookup_bool(&cfg, "fading", &ival))
*fading_enable = ival;
// --no-fading-open-close
lcfg_lookup_bool(&cfg, "no-fading-openclose", &ps->o.no_fading_openclose);
lcfg_lookup_bool(&cfg, "no-fading-openclose", &opt->no_fading_openclose);
// --no-fading-destroyed-argb
lcfg_lookup_bool(&cfg, "no-fading-destroyed-argb",
&ps->o.no_fading_destroyed_argb);
&opt->no_fading_destroyed_argb);
// --shadow-red
config_lookup_float(&cfg, "shadow-red", &ps->o.shadow_red);
config_lookup_float(&cfg, "shadow-red", &opt->shadow_red);
// --shadow-green
config_lookup_float(&cfg, "shadow-green", &ps->o.shadow_green);
config_lookup_float(&cfg, "shadow-green", &opt->shadow_green);
// --shadow-blue
config_lookup_float(&cfg, "shadow-blue", &ps->o.shadow_blue);
config_lookup_float(&cfg, "shadow-blue", &opt->shadow_blue);
// --shadow-exclude-reg
if (config_lookup_string(&cfg, "shadow-exclude-reg", &sval))
ps->o.shadow_exclude_reg_str = strdup(sval);
opt->shadow_exclude_reg_str = strdup(sval);
// --inactive-opacity-override
lcfg_lookup_bool(&cfg, "inactive-opacity-override",
&ps->o.inactive_opacity_override);
&opt->inactive_opacity_override);
// --inactive-dim
config_lookup_float(&cfg, "inactive-dim", &ps->o.inactive_dim);
config_lookup_float(&cfg, "inactive-dim", &opt->inactive_dim);
// --mark-wmwin-focused
lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &ps->o.mark_wmwin_focused);
lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &opt->mark_wmwin_focused);
// --mark-ovredir-focused
lcfg_lookup_bool(&cfg, "mark-ovredir-focused",
&ps->o.mark_ovredir_focused);
&opt->mark_ovredir_focused);
// --shadow-ignore-shaped
lcfg_lookup_bool(&cfg, "shadow-ignore-shaped",
&ps->o.shadow_ignore_shaped);
&opt->shadow_ignore_shaped);
// --detect-rounded-corners
lcfg_lookup_bool(&cfg, "detect-rounded-corners",
&ps->o.detect_rounded_corners);
&opt->detect_rounded_corners);
// --xinerama-shadow-crop
lcfg_lookup_bool(&cfg, "xinerama-shadow-crop",
&ps->o.xinerama_shadow_crop);
&opt->xinerama_shadow_crop);
// --detect-client-opacity
lcfg_lookup_bool(&cfg, "detect-client-opacity",
&ps->o.detect_client_opacity);
&opt->detect_client_opacity);
// --refresh-rate
config_lookup_int(&cfg, "refresh-rate", &ps->o.refresh_rate);
config_lookup_int(&cfg, "refresh-rate", &opt->refresh_rate);
// --vsync
if (config_lookup_string(&cfg, "vsync", &sval) && !parse_vsync(ps, sval))
exit(1);
if (config_lookup_string(&cfg, "vsync", &sval)) {
opt->vsync = parse_vsync(sval);
if (opt->vsync >= NUM_VSYNC) {
log_fatal("Cannot parse vsync");
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);
if (config_lookup_string(&cfg, "backend", &sval)) {
opt->backend = parse_backend(sval);
if (opt->backend >= NUM_BKEND) {
log_fatal("Cannot parse backend");
exit(1);
}
}
// --log-level
if (config_lookup_string(&cfg, "log-level", &sval)) {
auto level = string_to_log_level(sval);
if (level == LOG_LEVEL_INVALID) {
log_warn("Invalid log level, defaults to WARN");
} else {
log_set_level_tls(level);
}
}
// --log-file
if (config_lookup_string(&cfg, "log-file", &sval)) {
if (*sval != '/') {
log_warn("The log-file in your configuration file is not an absolute path");
}
opt->logpath = strdup(sval);
}
// --sw-opti
lcfg_lookup_bool(&cfg, "sw-opti", &ps->o.sw_opti);
lcfg_lookup_bool(&cfg, "sw-opti", &opt->sw_opti);
// --use-ewmh-active-win
lcfg_lookup_bool(&cfg, "use-ewmh-active-win",
&ps->o.use_ewmh_active_win);
&opt->use_ewmh_active_win);
// --unredir-if-possible
lcfg_lookup_bool(&cfg, "unredir-if-possible",
&ps->o.unredir_if_possible);
&opt->unredir_if_possible);
// --unredir-if-possible-delay
if (config_lookup_int(&cfg, "unredir-if-possible-delay", &ival))
ps->o.unredir_if_possible_delay = ival;
opt->unredir_if_possible_delay = ival;
// --inactive-dim-fixed
lcfg_lookup_bool(&cfg, "inactive-dim-fixed", &ps->o.inactive_dim_fixed);
lcfg_lookup_bool(&cfg, "inactive-dim-fixed", &opt->inactive_dim_fixed);
// --detect-transient
lcfg_lookup_bool(&cfg, "detect-transient", &ps->o.detect_transient);
lcfg_lookup_bool(&cfg, "detect-transient", &opt->detect_transient);
// --detect-client-leader
lcfg_lookup_bool(&cfg, "detect-client-leader",
&ps->o.detect_client_leader);
&opt->detect_client_leader);
// --shadow-exclude
parse_cfg_condlst(ps, &cfg, &ps->o.shadow_blacklist, "shadow-exclude");
parse_cfg_condlst(&cfg, &opt->shadow_blacklist, "shadow-exclude");
// --fade-exclude
parse_cfg_condlst(ps, &cfg, &ps->o.fade_blacklist, "fade-exclude");
parse_cfg_condlst(&cfg, &opt->fade_blacklist, "fade-exclude");
// --focus-exclude
parse_cfg_condlst(ps, &cfg, &ps->o.focus_blacklist, "focus-exclude");
parse_cfg_condlst(&cfg, &opt->focus_blacklist, "focus-exclude");
// --invert-color-include
parse_cfg_condlst(ps, &cfg, &ps->o.invert_color_list, "invert-color-include");
parse_cfg_condlst(&cfg, &opt->invert_color_list, "invert-color-include");
// --blur-background-exclude
parse_cfg_condlst(ps, &cfg, &ps->o.blur_background_blacklist, "blur-background-exclude");
parse_cfg_condlst(&cfg, &opt->blur_background_blacklist, "blur-background-exclude");
// --opacity-rule
parse_cfg_condlst_opct(ps, &cfg, "opacity-rule");
parse_cfg_condlst_opct(opt, &cfg, "opacity-rule");
// --unredir-if-possible-exclude
parse_cfg_condlst(ps, &cfg, &ps->o.unredir_if_possible_blacklist, "unredir-if-possible-exclude");
parse_cfg_condlst(&cfg, &opt->unredir_if_possible_blacklist, "unredir-if-possible-exclude");
// --blur-background
lcfg_lookup_bool(&cfg, "blur-background", &ps->o.blur_background);
lcfg_lookup_bool(&cfg, "blur-background", &opt->blur_background);
// --blur-background-frame
lcfg_lookup_bool(&cfg, "blur-background-frame",
&ps->o.blur_background_frame);
&opt->blur_background_frame);
// --blur-background-fixed
lcfg_lookup_bool(&cfg, "blur-background-fixed",
&ps->o.blur_background_fixed);
&opt->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))
if (config_lookup_string(&cfg, "blur-kern", &sval) &&
!parse_conv_kern_lst(sval, opt->blur_kerns, MAX_BLUR_PASS, conv_kern_hasneg)) {
log_fatal("Cannot parse \"blur-kern\"");
exit(1);
}
// --resize-damage
config_lookup_int(&cfg, "resize-damage", &ps->o.resize_damage);
config_lookup_int(&cfg, "resize-damage", &opt->resize_damage);
// --glx-no-stencil
lcfg_lookup_bool(&cfg, "glx-no-stencil", &ps->o.glx_no_stencil);
lcfg_lookup_bool(&cfg, "glx-no-stencil", &opt->glx_no_stencil);
// --glx-no-rebind-pixmap
lcfg_lookup_bool(&cfg, "glx-no-rebind-pixmap", &ps->o.glx_no_rebind_pixmap);
lcfg_lookup_bool(&cfg, "glx-no-rebind-pixmap", &opt->glx_no_rebind_pixmap);
// --glx-swap-method
if (config_lookup_string(&cfg, "glx-swap-method", &sval)
&& !parse_glx_swap_method(ps, sval))
exit(1);
if (config_lookup_string(&cfg, "glx-swap-method", &sval)) {
opt->glx_swap_method = parse_glx_swap_method(sval);
if (opt->glx_swap_method == -2) {
log_fatal("Cannot parse \"glx-swap-method\"");
exit(1);
}
}
// --glx-use-gpushader4
lcfg_lookup_bool(&cfg, "glx-use-gpushader4", &ps->o.glx_use_gpushader4);
lcfg_lookup_bool(&cfg, "glx-use-gpushader4", &opt->glx_use_gpushader4);
// --xrender-sync
lcfg_lookup_bool(&cfg, "xrender-sync", &ps->o.xrender_sync);
lcfg_lookup_bool(&cfg, "xrender-sync", &opt->xrender_sync);
// --xrender-sync-fence
lcfg_lookup_bool(&cfg, "xrender-sync-fence", &ps->o.xrender_sync_fence);
lcfg_lookup_bool(&cfg, "xrender-sync-fence", &opt->xrender_sync_fence);
if (lcfg_lookup_bool(&cfg, "clear-shadow", &bval))
printf_errf("(): \"clear-shadow\" is removed as an option, and is always"
" enabled now. Consider removing it from your config file");
log_warn("\"clear-shadow\" is removed as an option, and is always"
" enabled now. Consider removing it from your config file");
if (lcfg_lookup_bool(&cfg, "paint-on-overlay", &bval))
printf_errf("(): \"paint-on-overlay\" has been removed as an option, and "
"is enabled whenever possible");
log_warn("\"paint-on-overlay\" has been removed as an option, and "
"is enabled whenever possible");
if (config_lookup_float(&cfg, "alpha-step", &dval))
log_warn("\"alpha-step\" has been removed, compton now tries to make use"
" of all alpha values");
const char *deprecation_message = "has been removed. If you encounter problems "
"without this feature, please feel free to open a bug report.";
"without this feature, please feel free to open a bug report";
if (lcfg_lookup_bool(&cfg, "glx-use-copysubbuffermesa", &bval) && bval)
printf_errf("(): \"glx-use-copysubbuffermesa\" %s", deprecation_message);
log_warn("\"glx-use-copysubbuffermesa\" %s", deprecation_message);
if (lcfg_lookup_bool(&cfg, "glx-copy-from-front", &bval) && bval)
printf_errf("(): \"glx-copy-from-front\" %s", deprecation_message);
log_warn("\"glx-copy-from-front\" %s", deprecation_message);
// Wintype settings
// XXX ! Refactor all the wintype_* arrays into a struct
for (wintype_t i = 0; i < NUM_WINTYPES; ++i) {
char *str = mstrjoin("wintypes.", WINTYPES[i]);
config_setting_t *setting = config_lookup(&cfg, str);
free(str);
win_option_t *o = &opt->wintype_option[i];
win_option_mask_t *mask = &winopt_mask[i];
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;
if (config_setting_lookup_bool(setting, "shadow", &ival)) {
o->shadow = ival;
mask->shadow = true;
}
if (config_setting_lookup_bool(setting, "fade", &ival)) {
o->fade = ival;
mask->fade = true;
}
if (config_setting_lookup_bool(setting, "focus", &ival)) {
o->focus = ival;
mask->focus = true;
}
if (config_setting_lookup_bool(setting, "full-shadow", &ival)) {
o->full_shadow = ival;
mask->full_shadow = true;
}
if (config_setting_lookup_bool(setting, "redir-ignore", &ival)) {
o->redir_ignore = ival;
mask->redir_ignore = true;
}
double fval;
if (config_setting_lookup_float(setting, "opacity", &fval))
ps->o.wintype_opacity[i] = normalize_d(fval);
if (config_setting_lookup_float(setting, "opacity", &fval)) {
o->opacity = normalize_d(fval);
mask->opacity = true;
}
}
}
config_destroy(&cfg);
return path;
}

View file

@ -9,8 +9,56 @@
*
*/
#include "dbus.h"
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include "common.h"
#include "compiler.h"
#include "win.h"
#include "string_utils.h"
#include "log.h"
#include "dbus.h"
struct cdbus_data {
/// DBus connection.
DBusConnection *dbus_conn;
/// DBus service name.
char *dbus_service;
};
// 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
#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 %#010x 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"
#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__))
static DBusHandlerResult
cdbus_process(DBusConnection *conn, DBusMessage *m, void *);
@ -37,7 +85,13 @@ cdbus_callback_watch_toggled(DBusWatch *watch, void *data);
* Initialize D-Bus connection.
*/
bool
cdbus_init(session_t *ps) {
cdbus_init(session_t *ps, const char *uniq) {
auto cd = cmalloc(struct cdbus_data);
cd->dbus_service = NULL;
// Set ps->dbus_data here because add_watch functions need it
ps->dbus_data = cd;
DBusError err = { };
// Initialize
@ -45,70 +99,87 @@ cdbus_init(session_t *ps) {
// 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);
cd->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);
log_error("D-Bus connection failed (%s).", err.message);
dbus_error_free(&err);
return false;
goto fail;
}
if (!ps->dbus_conn) {
printf_errf("(): D-Bus connection failed for unknown reason.");
return false;
if (!cd->dbus_conn) {
log_error("D-Bus connection failed for unknown reason.");
goto fail;
}
// Avoid exiting on disconnect
dbus_connection_set_exit_on_disconnect(ps->dbus_conn, false);
dbus_connection_set_exit_on_disconnect(cd->dbus_conn, false);
// Request service name
{
// Build service name
char *service = mstrjoin3(CDBUS_SERVICE_NAME, ".", ps->o.display_repr);
ps->dbus_service = service;
size_t service_len = strlen(CDBUS_SERVICE_NAME)+strlen(uniq)+2;
char *service = ccalloc(service_len, char);
snprintf(service, service_len, "%s.%s", CDBUS_SERVICE_NAME, uniq);
// Make a valid dbus name by converting non alphanumeric characters to underscore
char *tmp = service + strlen(CDBUS_SERVICE_NAME)+1;
while (*tmp) {
if (!isalnum(*tmp)) {
*tmp = '_';
}
tmp++;
}
cd->dbus_service = service;
// Request for the name
int ret = dbus_bus_request_name(ps->dbus_conn, service,
int ret = dbus_bus_request_name(cd->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);
log_error("Failed to obtain D-Bus name (%s).", err.message);
dbus_error_free(&err);
goto fail;
}
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);
log_error("Failed to become the primary owner of requested D-Bus name (%d).", ret);
goto fail;
}
}
// Add watch handlers
if (!dbus_connection_set_watch_functions(ps->dbus_conn,
if (!dbus_connection_set_watch_functions(cd->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;
log_error("Failed to add D-Bus watch functions.");
goto fail;
}
// Add timeout handlers
if (!dbus_connection_set_timeout_functions(ps->dbus_conn,
if (!dbus_connection_set_timeout_functions(cd->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;
log_error("Failed to add D-Bus timeout functions.");
goto fail;
}
// Add match
dbus_bus_add_match(ps->dbus_conn,
dbus_bus_add_match(cd->dbus_conn,
"type='method_call',interface='" CDBUS_INTERFACE_NAME "'", &err);
if (dbus_error_is_set(&err)) {
printf_errf("(): Failed to add D-Bus match.");
log_error("Failed to add D-Bus match.");
dbus_error_free(&err);
return false;
goto fail;
}
dbus_connection_add_filter(ps->dbus_conn, cdbus_process, ps, NULL);
dbus_connection_add_filter(cd->dbus_conn, cdbus_process, ps, NULL);
return true;
fail:
ps->dbus_data = NULL;
free(cd->dbus_service);
free(cd);
return false;
}
/**
@ -116,24 +187,25 @@ cdbus_init(session_t *ps) {
*/
void
cdbus_destroy(session_t *ps) {
if (ps->dbus_conn) {
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn) {
// Release DBus name firstly
if (ps->dbus_service) {
if (cd->dbus_service) {
DBusError err = { };
dbus_error_init(&err);
dbus_bus_release_name(ps->dbus_conn, ps->dbus_service, &err);
dbus_bus_release_name(cd->dbus_conn, cd->dbus_service, &err);
if (dbus_error_is_set(&err)) {
printf_errf("(): Failed to release DBus name (%s).",
err.message);
log_error("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);
dbus_connection_close(cd->dbus_conn);
dbus_connection_unref(cd->dbus_conn);
}
free(cd);
}
/** @name DBusTimeout handling
@ -161,7 +233,7 @@ static dbus_bool_t
cdbus_callback_add_timeout(DBusTimeout *timeout, void *data) {
session_t *ps = data;
ev_dbus_timer *t = calloc(1, sizeof *t);
auto t = ccalloc(1, ev_dbus_timer);
double i = dbus_timeout_get_interval(timeout) / 1000.0;
ev_timer_init(&t->w, cdbus_callback_handle_timeout, i, i);
t->t = timeout;
@ -211,7 +283,7 @@ cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data) {
typedef struct ev_dbus_io {
ev_io w;
session_t *ps;
struct cdbus_data *cd;
DBusWatch *dw;
} ev_dbus_io;
@ -223,7 +295,7 @@ void cdbus_io_callback(EV_P_ ev_io *w, int revents) {
if (revents & EV_WRITE)
flags |= DBUS_WATCH_WRITABLE;
dbus_watch_handle(dw->dw, flags);
while (dbus_connection_dispatch(dw->ps->dbus_conn) != DBUS_DISPATCH_COMPLETE);
while (dbus_connection_dispatch(dw->cd->dbus_conn) != DBUS_DISPATCH_COMPLETE);
}
/**
@ -248,9 +320,9 @@ static dbus_bool_t
cdbus_callback_add_watch(DBusWatch *watch, void *data) {
session_t *ps = data;
ev_dbus_io *w = calloc(1, sizeof *w);
auto w = ccalloc(1, ev_dbus_io);
w->dw = watch;
w->ps = ps;
w->cd = ps->dbus_data;
ev_io_init(&w->w, cdbus_io_callback, dbus_watch_get_unix_fd(watch),
cdbus_get_watch_cond(watch));
@ -305,7 +377,7 @@ cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data) {
if (!dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &val,
DBUS_TYPE_INVALID)) {
printf_errf("(): Failed to append argument.");
log_error("Failed to append argument.");
return false;
}
@ -319,7 +391,7 @@ 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.");
log_error("Failed to append argument.");
return false;
}
@ -333,7 +405,7 @@ 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.");
log_error("Failed to append argument.");
return false;
}
@ -347,7 +419,7 @@ 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.");
log_error("Failed to append argument.");
return false;
}
@ -364,7 +436,7 @@ cdbus_apdarg_wid(session_t *ps, DBusMessage *msg, const void *data) {
if (!dbus_message_append_args(msg, CDBUS_TYPE_WINDOW, &val,
DBUS_TYPE_INVALID)) {
printf_errf("(): Failed to append argument.");
log_error("Failed to append argument.");
return false;
}
@ -379,7 +451,7 @@ 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.");
log_error("Failed to append argument.");
return false;
}
@ -397,7 +469,7 @@ cdbus_apdarg_string(session_t *ps, DBusMessage *msg, const void *data) {
if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &str,
DBUS_TYPE_INVALID)) {
printf_errf("(): Failed to append argument.");
log_error("Failed to append argument.");
return false;
}
@ -417,11 +489,7 @@ cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data) {
}
// 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;
}
auto arr = ccalloc(count, cdbus_window_t);
// Build the array
{
@ -439,7 +507,7 @@ cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data) {
// 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.");
log_error("Failed to append argument.");
free(arr);
return false;
}
@ -462,13 +530,14 @@ static bool
cdbus_signal(session_t *ps, const char *name,
bool (*func)(session_t *ps, DBusMessage *msg, const void *data),
const void *data) {
struct cdbus_data *cd = ps->dbus_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.");
log_error("Failed to create D-Bus signal.");
return false;
}
@ -479,12 +548,12 @@ cdbus_signal(session_t *ps, const char *name,
}
// Send the message and flush the connection
if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) {
printf_errf("(): Failed to send D-Bus signal.");
if (!dbus_connection_send(cd->dbus_conn, msg, NULL)) {
log_error("Failed to send D-Bus signal.");
dbus_message_unref(msg);
return false;
}
dbus_connection_flush(ps->dbus_conn);
dbus_connection_flush(cd->dbus_conn);
// Free the message
dbus_message_unref(msg);
@ -492,6 +561,14 @@ cdbus_signal(session_t *ps, const char *name,
return true;
}
/**
* 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);
}
/**
* Send a D-Bus reply.
*
@ -505,12 +582,13 @@ static bool
cdbus_reply(session_t *ps, DBusMessage *srcmsg,
bool (*func)(session_t *ps, DBusMessage *msg, const void *data),
const void *data) {
struct cdbus_data *cd = ps->dbus_data;
DBusMessage* msg = NULL;
// Create a reply
msg = dbus_message_new_method_return(srcmsg);
if (!msg) {
printf_errf("(): Failed to create D-Bus reply.");
log_error("Failed to create D-Bus reply.");
return false;
}
@ -521,12 +599,12 @@ cdbus_reply(session_t *ps, DBusMessage *srcmsg,
}
// Send the message and flush the connection
if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) {
printf_errf("(): Failed to send D-Bus reply.");
if (!dbus_connection_send(cd->dbus_conn, msg, NULL)) {
log_error("Failed to send D-Bus reply.");
dbus_message_unref(msg);
return false;
}
dbus_connection_flush(ps->dbus_conn);
dbus_connection_flush(cd->dbus_conn);
// Free the message
dbus_message_unref(msg);
@ -534,6 +612,62 @@ cdbus_reply(session_t *ps, DBusMessage *srcmsg,
return true;
}
/**
* 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);
}
/**
* Send a D-Bus error reply.
*
@ -542,18 +676,19 @@ cdbus_reply(session_t *ps, DBusMessage *srcmsg,
*/
static bool
cdbus_reply_errm(session_t *ps, DBusMessage *msg) {
struct cdbus_data *cd = ps->dbus_data;
if (!msg) {
printf_errf("(): Failed to create D-Bus reply.");
log_error("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.");
if (!dbus_connection_send(cd->dbus_conn, msg, NULL)) {
log_error("Failed to send D-Bus reply.");
dbus_message_unref(msg);
return false;
}
dbus_connection_flush(ps->dbus_conn);
dbus_connection_flush(cd->dbus_conn);
// Free the message
dbus_message_unref(msg);
@ -575,7 +710,7 @@ cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest) {
DBusMessageIter iter = { };
if (!dbus_message_iter_init(msg, &iter)) {
printf_errf("(): Message has no argument.");
log_error("Message has no argument.");
return false;
}
@ -583,7 +718,7 @@ cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest) {
const int oldcount = count;
while (count) {
if (!dbus_message_iter_next(&iter)) {
printf_errf("(): Failed to find argument %d.", oldcount);
log_error("Failed to find argument %d.", oldcount);
return false;
}
--count;
@ -591,7 +726,7 @@ cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest) {
}
if (type != dbus_message_iter_get_arg_type(&iter)) {
printf_errf("(): Argument has incorrect type.");
log_error("Argument has incorrect type.");
return false;
}
@ -627,8 +762,7 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) {
CDBUS_TYPE_WINDOW, &wid,
DBUS_TYPE_STRING, &target,
DBUS_TYPE_INVALID)) {
printf_errf("(): Failed to parse argument of \"win_get\" (%s).",
err.message);
log_error("Failed to parse argument of \"win_get\" (%s).", err.message);
dbus_error_free(&err);
return false;
}
@ -636,7 +770,7 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) {
win *w = find_win(ps, wid);
if (!w) {
printf_errf("(): Window %#010x not found.", wid);
log_error("Window %#010x not found.", wid);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
return true;
}
@ -713,7 +847,7 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) {
cdbus_m_win_get_do(blur_background, cdbus_reply_bool);
#undef cdbus_m_win_get_do
printf_errf("(): " CDBUS_ERROR_BADTGT_S, target);
log_error(CDBUS_ERROR_BADTGT_S, target);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
return true;
@ -732,8 +866,7 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg) {
CDBUS_TYPE_WINDOW, &wid,
DBUS_TYPE_STRING, &target,
DBUS_TYPE_INVALID)) {
printf_errf("(): Failed to parse argument of \"win_set\" (%s).",
err.message);
log_error("(): Failed to parse argument of \"win_set\" (%s).", err.message);
dbus_error_free(&err);
return false;
}
@ -741,7 +874,7 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg) {
win *w = find_win(ps, wid);
if (!w) {
printf_errf("(): Window %#010x not found.", wid);
log_error("Window %#010x not found.", wid);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid);
return true;
}
@ -788,7 +921,7 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg) {
}
#undef cdbus_m_win_set_do
printf_errf("(): " CDBUS_ERROR_BADTGT_S, target);
log_error(CDBUS_ERROR_BADTGT_S, target);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
return true;
@ -827,7 +960,7 @@ cdbus_process_find_win(session_t *ps, DBusMessage *msg) {
wid = w->id;
}
else {
printf_errf("(): " CDBUS_ERROR_BADTGT_S, target);
log_error(CDBUS_ERROR_BADTGT_S, target);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
return true;
@ -879,7 +1012,6 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) {
}
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);
@ -896,7 +1028,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) {
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_stub(synchronize, cdbus_reply_bool, false);
cdbus_m_opts_get_do(refresh_rate, cdbus_reply_int32);
cdbus_m_opts_get_do(sw_opti, cdbus_reply_bool);
@ -953,7 +1085,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) {
#undef cdbus_m_opts_get_do
#undef cdbus_m_opts_get_stub
printf_errf("(): " CDBUS_ERROR_BADTGT_S, target);
log_error(CDBUS_ERROR_BADTGT_S, target);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
return true;
@ -1051,15 +1183,27 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
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.");
auto tmp_vsync = parse_vsync(val);
if (tmp_vsync >= NUM_VSYNC) {
log_error("Failed to parse vsync: invalid value %s.", val);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADARG, CDBUS_ERROR_BADARG_S, 1,
"Value invalid.");
return true;
}
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
auto old_vsync = ps->o.vsync;
ps->o.vsync = tmp_vsync;
if (!vsync_init(ps)) {
// Trying to revert back to original vsync values
log_error("Failed to initialize specified VSync method.");
ps->o.vsync = old_vsync;
if (!vsync_init(ps)) {
log_error("Failed to revert back to original VSync method.");
ps->o.vsync = VSYNC_NONE;
}
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;
}
@ -1079,7 +1223,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
#undef cdbus_m_opts_set_do
printf_errf("(): " CDBUS_ERROR_BADTGT_S, target);
log_error(CDBUS_ERROR_BADTGT_S, target);
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
return true;
@ -1206,13 +1350,13 @@ cdbus_process(DBusConnection *c, DBusMessage *msg, void *ud) {
}
else {
if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(msg)) {
printf_errf("(): Error message of path \"%s\" "
log_error("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\" "
log_error("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));
@ -1239,37 +1383,43 @@ cdbus_process(DBusConnection *c, DBusMessage *msg, void *ud) {
///@{
void
cdbus_ev_win_added(session_t *ps, win *w) {
if (ps->dbus_conn)
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn)
cdbus_signal_wid(ps, "win_added", w->id);
}
void
cdbus_ev_win_destroyed(session_t *ps, win *w) {
if (ps->dbus_conn)
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn)
cdbus_signal_wid(ps, "win_destroyed", w->id);
}
void
cdbus_ev_win_mapped(session_t *ps, win *w) {
if (ps->dbus_conn)
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn)
cdbus_signal_wid(ps, "win_mapped", w->id);
}
void
cdbus_ev_win_unmapped(session_t *ps, win *w) {
if (ps->dbus_conn)
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn)
cdbus_signal_wid(ps, "win_unmapped", w->id);
}
void
cdbus_ev_win_focusout(session_t *ps, win *w) {
if (ps->dbus_conn)
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn)
cdbus_signal_wid(ps, "win_focusout", w->id);
}
void
cdbus_ev_win_focusin(session_t *ps, win *w) {
if (ps->dbus_conn)
struct cdbus_data *cd = ps->dbus_data;
if (cd->dbus_conn)
cdbus_signal_wid(ps, "win_focusin", w->id);
}
//!@}

View file

@ -9,181 +9,46 @@
*
*/
#include "common.h"
#include <ctype.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdbool.h>
#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 %#010x 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"
#include <dbus/dbus.h>
// 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 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);
typedef struct session session_t;
typedef struct win win;
/**
* 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));
static inline const char *cdbus_repr_msgtype(DBusMessage *msg) {
return dbus_message_type_to_string(dbus_message_get_type(msg));
}
///@}
/**
* Initialize D-Bus connection.
*/
bool cdbus_init(session_t *ps, const char *uniq_name);
/**
* Destroy D-Bus connection.
*/
void cdbus_destroy(session_t *ps);
/// Generate dbus win_added signal
void cdbus_ev_win_added(session_t *ps, win *w);
/// Generate dbus win_destroyed signal
void cdbus_ev_win_destroyed(session_t *ps, win *w);
/// Generate dbus win_mapped signal
void cdbus_ev_win_mapped(session_t *ps, win *w);
/// Generate dbus win_unmapped signal
void cdbus_ev_win_unmapped(session_t *ps, win *w);
/// Generate dbus win_focusout signal
void cdbus_ev_win_focusout(session_t *ps, win *w);
/// Generate dbus win_focusin signal
void cdbus_ev_win_focusin(session_t *ps, win *w);
// vim: set noet sw=8 ts=8 :

View file

@ -17,6 +17,7 @@ void print_diagnostics(session_t *ps) {
#ifdef __FAST_MATH__
printf("* Fast Math: Yes\n");
#endif
printf("* Config file used: %s\n", ps->o.config_file ?: "None");
}
// vim: set noet sw=8 ts=8 :

116
src/kernel.c Normal file
View file

@ -0,0 +1,116 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#include <math.h>
#include "kernel.h"
#include "utils.h"
/*
* A picture will help
*
* -center 0 width width+center
* -center +-----+-------------------+-----+
* | | | |
* | | | |
* 0 +-----+-------------------+-----+
* | | | |
* | | | |
* | | | |
* height +-----+-------------------+-----+
* | | | |
* height+ | | | |
* center +-----+-------------------+-----+
*/
double sum_kernel(const conv *map, int x, int y, int width,
int height) {
int fx, fy;
const double *g_data;
const 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 v;
}
static double attr_const gaussian(double r, double x, double y) {
// Formula can be found here:
// https://en.wikipedia.org/wiki/Gaussian_blur#Mathematics
// Except a special case for r == 0 to produce sharp shadows
if (r == 0)
return 1;
return exp(-0.5 * (x * x + y * y) / (r * r)) / (2 * M_PI * r * r);
}
conv *gaussian_kernel(double r) {
conv *c;
int size = r * 2 + 1;
int center = size / 2;
double t;
c = cvalloc(sizeof(conv) + size * size * sizeof(double));
c->size = size;
t = 0.0;
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
double g = gaussian(r, x - center, y - center);
t += g;
c->data[y * size + x] = g;
}
}
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
c->data[y * size + x] /= t;
}
}
return c;
}
// vim: set noet sw=8 ts=8 :

19
src/kernel.h Normal file
View file

@ -0,0 +1,19 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include "compiler.h"
/// Code for generating convolution kernels
typedef struct conv {
int size;
double data[];
} conv;
/// Calculate the sum of a rectangle part of the convolution kernel
/// the rectangle is defined by top left (x, y), and a size (width x height)
double attr_const sum_kernel(const conv *map, int x, int y, int width, int height);
/// Create a kernel with gaussian distribution of radius r
conv *gaussian_kernel(double r);

341
src/log.c Normal file
View file

@ -0,0 +1,341 @@
#include <assert.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/uio.h>
#include <time.h>
#include <unistd.h>
#ifdef CONFIG_OPENGL
#include <GL/glx.h>
#endif
#include "compiler.h"
#include "log.h"
#include "utils.h"
thread_local struct log *tls_logger;
struct log_target;
struct log {
struct log_target *head;
int log_level;
};
struct log_target {
const struct log_ops *ops;
struct log_target *next;
};
struct log_ops {
void (*write)(struct log_target *, const char *, size_t);
void (*writev)(struct log_target *, const struct iovec *, int vcnt);
void (*destroy)(struct log_target *);
/// Additional strings to print around the log_level string
const char *(*colorize_begin)(enum log_level);
const char *(*colorize_end)(enum log_level);
};
/// Fallback writev for targets don't implement it
static attr_unused void
log_default_writev(struct log_target *tgt, const struct iovec *vec, int vcnt) {
size_t total = 0;
for (int i = 0; i < vcnt; i++) {
total += vec[i].iov_len;
}
char *buf = ccalloc(total, char);
total = 0;
for (int i = 0; i < vcnt; i++) {
memcpy(buf + total, vec[i].iov_base, vec[i].iov_len);
total += vec[i].iov_len;
}
tgt->ops->write(tgt, buf, total);
free(buf);
}
static attr_const const char *log_level_to_string(enum log_level level) {
switch (level) {
case LOG_LEVEL_TRACE: return "TRACE";
case LOG_LEVEL_DEBUG: return "DEBUG";
case LOG_LEVEL_INFO: return "INFO";
case LOG_LEVEL_WARN: return "WARN";
case LOG_LEVEL_ERROR: return "ERROR";
case LOG_LEVEL_FATAL: return "FATAL ERROR";
default: assert(false);
}
}
enum log_level string_to_log_level(const char *str) {
if (strcasecmp(str, "TRACE") == 0)
return LOG_LEVEL_TRACE;
else if (strcasecmp(str, "DEBUG") == 0)
return LOG_LEVEL_DEBUG;
else if (strcasecmp(str, "INFO") == 0)
return LOG_LEVEL_INFO;
else if (strcasecmp(str, "WARN") == 0)
return LOG_LEVEL_WARN;
else if (strcasecmp(str, "ERROR") == 0)
return LOG_LEVEL_ERROR;
return LOG_LEVEL_INVALID;
}
struct log *log_new(void) {
auto ret = cmalloc(struct log);
ret->log_level = LOG_LEVEL_WARN;
ret->head = NULL;
return ret;
}
void log_add_target(struct log *l, struct log_target *tgt) {
assert(tgt->ops->writev);
tgt->next = l->head;
l->head = tgt;
}
/// Destroy a log struct
void log_destroy(struct log *l) {
// free all tgt
struct log_target *head = l->head;
while (head) {
auto next = head->next;
head->ops->destroy(head);
head = next;
}
free(l);
}
void log_set_level(struct log *l, int level) {
assert(level <= LOG_LEVEL_FATAL && level >= 0);
l->log_level = level;
}
enum log_level log_get_level(const struct log *l) {
return l->log_level;
}
attr_printf(4, 5) void log_printf(struct log *l, int level, const char *func,
const char *fmt, ...) {
assert(level <= LOG_LEVEL_FATAL && level >= 0);
if (level < l->log_level)
return;
char *buf = NULL;
va_list args;
va_start(args, fmt);
size_t blen = vasprintf(&buf, fmt, args);
va_end(args);
if (!buf)
return;
struct timespec ts;
timespec_get(&ts, TIME_UTC);
auto tm = localtime(&ts.tv_sec);
char time_buf[100];
strftime(time_buf, sizeof time_buf, "%x %T", tm);
char *time = NULL;
size_t tlen = asprintf(&time, "%s.%03ld", time_buf, ts.tv_nsec / 1000000);
if (!time) {
free(buf);
return;
}
const char *log_level_str = log_level_to_string(level);
size_t llen = strlen(log_level_str);
size_t flen = strlen(func);
struct log_target *head = l->head;
while (head) {
const char *p = "", *s = "";
size_t plen = 0, slen = 0;
if (head->ops->colorize_begin) {
// construct target specific prefix
p = head->ops->colorize_begin(level);
plen = strlen(p);
if (head->ops->colorize_end) {
s = head->ops->colorize_end(level);
slen = strlen(s);
}
}
head->ops->writev(
head,
(struct iovec[]){{.iov_base = "[ ", .iov_len = 2},
{.iov_base = time, .iov_len = tlen},
{.iov_base = " ", .iov_len = 1},
{.iov_base = (void *)func, .iov_len = flen},
{.iov_base = " ", .iov_len = 1},
{.iov_base = (void *)p, .iov_len = plen},
{.iov_base = (void *)log_level_str, .iov_len = llen},
{.iov_base = (void *)s, .iov_len = slen},
{.iov_base = " ] ", .iov_len = 3},
{.iov_base = buf, .iov_len = blen},
{.iov_base = "\n", .iov_len = 1}},
11);
head = head->next;
}
free(time);
free(buf);
}
/// A trivial deinitializer that simply frees the memory
static attr_unused void logger_trivial_destroy(struct log_target *tgt) {
free(tgt);
}
/// A null log target that does nothing
static const struct log_ops null_logger_ops;
static struct log_target null_logger_target = {
.ops = &null_logger_ops,
};
struct log_target *null_logger_new(void) {
return &null_logger_target;
}
static void null_logger_write(struct log_target *attr_unused tgt,
const char *attr_unused str, size_t attr_unused len) {
return;
}
static void null_logger_writev(struct log_target *attr_unused tgt,
const struct iovec *attr_unused vec, int attr_unused vcnt) {
return;
}
static const struct log_ops null_logger_ops = {
.write = null_logger_write,
.writev = null_logger_writev,
};
/// A file based logger that writes to file (or stdout/stderr)
struct file_logger {
struct log_target tgt;
FILE *f;
struct log_ops ops;
};
void file_logger_write(struct log_target *tgt, const char *str, size_t len) {
auto f = (struct file_logger *)tgt;
fwrite(str, 1, len, f->f);
}
void file_logger_writev(struct log_target *tgt, const struct iovec *vec, int vcnt) {
auto f = (struct file_logger *)tgt;
fflush(f->f);
writev(fileno(f->f), vec, vcnt);
}
void file_logger_destroy(struct log_target *tgt) {
auto f = (struct file_logger *)tgt;
fclose(f->f);
free(tgt);
}
#define ANSI(x) "\033[" x "m"
const char *terminal_colorize_begin(enum log_level level) {
switch (level) {
case LOG_LEVEL_TRACE: return ANSI("30;2");
case LOG_LEVEL_DEBUG: return ANSI("37;2");
case LOG_LEVEL_INFO: return ANSI("92");
case LOG_LEVEL_WARN: return ANSI("33");
case LOG_LEVEL_ERROR: return ANSI("31;1");
case LOG_LEVEL_FATAL: return ANSI("30;103;1");
default: assert(false);
}
}
const char *terminal_colorize_end(enum log_level level) {
return ANSI("0");
}
#undef PREFIX
static const struct log_ops file_logger_ops = {
.write = file_logger_write,
.writev = file_logger_writev,
.destroy = file_logger_destroy,
};
struct log_target *file_logger_new(const char *filename) {
FILE *f = fopen(filename, "a");
if (!f) {
return NULL;
}
auto ret = cmalloc(struct file_logger);
ret->tgt.ops = &ret->ops;
ret->f = f;
// Always assume a file is not a terminal
ret->ops = file_logger_ops;
return &ret->tgt;
}
struct log_target *stderr_logger_new(void) {
int fd = dup(STDERR_FILENO);
if (fd < 0) {
return NULL;
}
FILE *f = fdopen(fd, "w");
if (!f) {
return NULL;
}
auto ret = cmalloc(struct file_logger);
ret->tgt.ops = &ret->ops;
ret->f = f;
ret->ops = file_logger_ops;
if (isatty(fd)) {
ret->ops.colorize_begin = terminal_colorize_begin;
ret->ops.colorize_end = terminal_colorize_end;
}
return &ret->tgt;
}
#ifdef CONFIG_OPENGL
/// An opengl logger that can be used for logging into opengl debugging tools,
/// such as apitrace
struct glx_string_marker_logger {
struct log_target tgt;
void (*glx_string_marker)(GLsizei len, const char *);
};
void glx_string_marker_logger_write(struct log_target *tgt, const char *str, size_t len) {
auto g = (struct glx_string_marker_logger *)tgt;
g->glx_string_marker(len, str);
}
static const struct log_ops glx_string_marker_logger_ops = {
.write = glx_string_marker_logger_write,
.writev = log_default_writev,
.destroy = logger_trivial_destroy,
};
struct log_target *glx_string_marker_logger_new(void) {
void *fnptr = glXGetProcAddress((GLubyte *)"glStringMarkerGREMEDY");
if (!fnptr)
return NULL;
auto ret = cmalloc(struct glx_string_marker_logger);
ret->tgt.ops = &glx_string_marker_logger_ops;
ret->glx_string_marker = fnptr;
return &ret->tgt;
}
#else
struct log_target *glx_string_marker_logger_new(void) {
return null_logger_new();
}
#endif
// vim: set noet sw=8 ts=8:

79
src/log.h Normal file
View file

@ -0,0 +1,79 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2018 Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <assert.h>
#include <stdio.h>
#include "compiler.h"
enum log_level {
LOG_LEVEL_INVALID = -1,
LOG_LEVEL_TRACE = 0,
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
LOG_LEVEL_WARN,
LOG_LEVEL_ERROR,
LOG_LEVEL_FATAL,
};
#define LOG(level, x, ...) \
do { \
if (LOG_LEVEL_##level >= log_get_level_tls()) { \
log_printf(tls_logger, LOG_LEVEL_##level, __func__, x, \
##__VA_ARGS__); \
} \
} while (0)
#define log_trace(x, ...) LOG(TRACE, x, ##__VA_ARGS__)
#define log_debug(x, ...) LOG(DEBUG, x, ##__VA_ARGS__)
#define log_info(x, ...) LOG(INFO, x, ##__VA_ARGS__)
#define log_warn(x, ...) LOG(WARN, x, ##__VA_ARGS__)
#define log_error(x, ...) LOG(ERROR, x, ##__VA_ARGS__)
#define log_fatal(x, ...) LOG(FATAL, x, ##__VA_ARGS__)
struct log;
struct log_target;
attr_printf(4, 5) void log_printf(struct log *, int level, const char *func,
const char *fmt, ...);
attr_malloc struct log *log_new(void);
attr_nonnull_all void log_destroy(struct log *);
attr_nonnull(1) void log_set_level(struct log *l, int level);
attr_pure enum log_level log_get_level(const struct log *l);
attr_nonnull_all void log_add_target(struct log *, struct log_target *);
attr_const enum log_level string_to_log_level(const char *);
extern thread_local struct log *tls_logger;
/// Create a thread local logger
static inline void log_init_tls(void) {
tls_logger = log_new();
}
/// Set thread local logger log level
static inline void log_set_level_tls(int level) {
assert(tls_logger);
log_set_level(tls_logger, level);
}
static inline attr_nonnull_all void log_add_target_tls(struct log_target *tgt) {
assert(tls_logger);
log_add_target(tls_logger, tgt);
}
static inline attr_pure enum log_level log_get_level_tls(void) {
assert(tls_logger);
return log_get_level(tls_logger);
}
static inline void log_deinit_tls(void) {
assert(tls_logger);
log_destroy(tls_logger);
tls_logger = NULL;
}
attr_malloc struct log_target *stderr_logger_new(void);
attr_malloc struct log_target *file_logger_new(const char *file);
attr_malloc struct log_target *null_logger_new(void);
attr_malloc struct log_target *glx_string_marker_logger_new(void);
// vim: set noet sw=8 ts=8:

View file

@ -1,13 +1,17 @@
deps = [
base_deps = [
cc.find_library('m'),
cc.find_library('ev'),
dependency('xcb', version: '>=1.9.2')
dependency('xcb', version: '>=1.9.2'),
]
srcs = ['compton.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'diagnostic.c']
srcs = [ files('compton.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', 'utils.c',
'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c', 'log.c',
'options.c') ]
compton_inc = include_directories('.')
cflags = []
required_package = [
'x11', 'x11-xcb', 'xcb-renderutil',
'xcb-render', 'xcb-damage', 'xcb-randr',
@ -16,16 +20,19 @@ required_package = [
]
foreach i : required_package
deps += [dependency(i, required: true)]
base_deps += [dependency(i, required: true)]
endforeach
deps = []
if get_option('xinerama')
deps += [dependency('xcb-xinerama', required: true)]
cflags += ['-DCONFIG_XINERAMA']
endif
if get_option('config_file')
deps += [dependency('libconfig', version: '>=1.4', required: true)]
deps += [dependency('libconfig', version: '>=1.4', required: true),
dependency('libxdg-basedir', required: true)]
cflags += ['-DCONFIG_LIBCONFIG']
srcs += [ 'config_libconfig.c' ]
endif
@ -44,7 +51,7 @@ if get_option('vsync_drm')
endif
if get_option('opengl')
cflags += ['-DCONFIG_OPENGL']
cflags += ['-DCONFIG_OPENGL', '-DGL_GLEXT_PROTOTYPES']
deps += [dependency('gl', required: true)]
srcs += [ 'opengl.c' ]
endif
@ -60,4 +67,8 @@ if get_option('xrescheck')
srcs += [ 'xrescheck.c' ]
endif
executable('compton', srcs, c_args: cflags, dependencies: deps, install: true)
subdir('backend')
executable('compton', srcs, c_args: cflags,
dependencies: [ base_deps, deps ],
install: true, include_directories: compton_inc)

View file

@ -9,6 +9,10 @@
*
*/
#include "compiler.h"
#include "string_utils.h"
#include "log.h"
#include "opengl.h"
static inline int
@ -70,12 +74,13 @@ glx_update_fbconfig_bydepth(session_t *ps, int depth, glx_fbconfig_t *pfbcfg) {
// 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
log_trace("(depth %d): %p overrides %p, target %#x.", depth,
pfbcfg->cfg,
ps->psglx->fbconfigs[depth] ? ps->psglx->fbconfigs[depth]->cfg:
0,
pfbcfg->texture_tgts);
if (!ps->psglx->fbconfigs[depth]) {
ps->psglx->fbconfigs[depth] = malloc(sizeof(glx_fbconfig_t));
allocchk(ps->psglx->fbconfigs[depth]);
ps->psglx->fbconfigs[depth] = cmalloc(glx_fbconfig_t);
}
(*ps->psglx->fbconfigs[depth]) = *pfbcfg;
}
@ -110,11 +115,11 @@ glx_update_fbconfig(session_t *ps) {
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);
log_error("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);
log_error("Failed to retrieve BIND_TO_TEXTURE_TARGETS_EXT of FBConfig %d.", id);
continue;
}
@ -123,7 +128,7 @@ glx_update_fbconfig(session_t *ps) {
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);
// log_error("Failed to retrieve X Visual of FBConfig %d.", id);
continue;
}
visualdepth = pvi->depth;
@ -160,19 +165,16 @@ glx_update_fbconfig(session_t *ps) {
// Sanity checks
if (!ps->psglx->fbconfigs[ps->depth]) {
printf_errf("(): No FBConfig found for default depth %d.", ps->depth);
log_error("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.");
log_error("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
log_trace("%d-bit: %p, 32-bit: %p", ps->depth, ps->psglx->fbconfigs[ps->depth]->cfg,
ps->psglx->fbconfigs[32]->cfg);
return true;
}
@ -205,8 +207,8 @@ 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);
log_trace("source 0x%04X, type 0x%04X, id %u, severity 0x%0X, \"%s\"",
source, type, id, severity, message);
}
#endif
@ -223,15 +225,20 @@ glx_init(session_t *ps, bool need_render) {
if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error))
ps->glx_exists = true;
else {
printf_errf("(): No GLX extension.");
log_error("No GLX extension.");
goto glx_init_end;
}
}
if (ps->o.glx_swap_method > CGLX_MAX_BUFFER_AGE) {
log_error("glx-swap-method is too big");
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.");
log_error("Failed to acquire XVisualInfo for current visual.");
goto glx_init_end;
}
@ -239,13 +246,13 @@ glx_init(session_t *ps, bool need_render) {
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.");
log_error("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.");
log_error("Root visual is not a double buffered GL visual.");
goto glx_init_end;
}
}
@ -257,7 +264,7 @@ glx_init(session_t *ps, bool need_render) {
// 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);
ps->psglx = cmalloc(glx_session_t);
memcpy(ps->psglx, &CGLX_SESSION_DEF, sizeof(glx_session_t));
for (int i = 0; i < MAX_BLUR_PASS; ++i) {
@ -278,8 +285,7 @@ glx_init(session_t *ps, bool need_render) {
{
GLXFBConfig fbconfig = get_fbconfig_from_visualinfo(ps, pvis);
if (!fbconfig) {
printf_errf("(): Failed to get GLXFBConfig for root visual %#lx.",
pvis->visualid);
log_error("Failed to get GLXFBConfig for root visual %#lx.", pvis->visualid);
goto glx_init_end;
}
@ -287,7 +293,7 @@ glx_init(session_t *ps, bool need_render) {
(f_glXCreateContextAttribsARB)
glXGetProcAddress((const GLubyte *) "glXCreateContextAttribsARB");
if (!p_glXCreateContextAttribsARB) {
printf_errf("(): Failed to get glXCreateContextAttribsARB().");
log_error("Failed to get glXCreateContextAttribsARB().");
goto glx_init_end;
}
@ -301,13 +307,13 @@ glx_init(session_t *ps, bool need_render) {
#endif
if (!psglx->context) {
printf_errf("(): Failed to get GLX context.");
log_error("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.");
log_error("Failed to attach GLX context.");
goto glx_init_end;
}
@ -317,7 +323,7 @@ glx_init(session_t *ps, bool need_render) {
(f_DebugMessageCallback)
glXGetProcAddress((const GLubyte *) "glDebugMessageCallback");
if (!p_DebugMessageCallback) {
printf_errf("(): Failed to get glDebugMessageCallback(0.");
log_error("Failed to get glDebugMessageCallback(0.");
goto glx_init_end;
}
p_DebugMessageCallback(glx_debug_msg_callback, ps);
@ -333,7 +339,7 @@ glx_init(session_t *ps, bool need_render) {
GLint val = 0;
glGetIntegerv(GL_STENCIL_BITS, &val);
if (!val) {
printf_errf("(): Target window doesn't have stencil buffer.");
log_error("Target window doesn't have stencil buffer.");
goto glx_init_end;
}
}
@ -358,7 +364,7 @@ glx_init(session_t *ps, bool need_render) {
psglx->glXReleaseTexImageProc = (f_ReleaseTexImageEXT)
glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT");
if (!psglx->glXBindTexImageProc || !psglx->glXReleaseTexImageProc) {
printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT().");
log_error("Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT().");
goto glx_init_end;
}
}
@ -435,7 +441,7 @@ glx_destroy(session_t *ps) {
glDeleteProgram(ppass->prog);
}
glx_free_prog_main(ps, &ps->o.glx_prog_win);
glx_free_prog_main(ps, &ps->glx_prog_win);
glx_check_err(ps);
@ -465,12 +471,12 @@ glx_reinit(session_t *ps, bool need_render) {
glx_destroy(ps);
if (!glx_init(ps, need_render)) {
printf_errf("(): Failed to initialize GLX.");
log_error("Failed to initialize GLX.");
return false;
}
if (!vsync_init(ps)) {
printf_errf("(): Failed to initialize VSync.");
log_error("Failed to initialize VSync.");
return false;
}
@ -505,15 +511,15 @@ glx_init_blur(session_t *ps) {
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
if (!fbo) {
printf_errf("(): Failed to generate Framebuffer. Cannot do "
"multi-pass blur with GLX backend.");
log_error("Failed to generate Framebuffer. Cannot do multi-pass blur with GLX"
" backend.");
return false;
}
glDeleteFramebuffers(1, &fbo);
}
{
char *lc_numeric_old = mstrcpy(setlocale(LC_NUMERIC, NULL));
char *lc_numeric_old = strdup(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");
@ -543,7 +549,7 @@ glx_init_blur(session_t *ps) {
const char *texture_func = (use_texture_rect ?
"texture2DRect": "texture2D");
const char *shader_add = FRAG_SHADER_BLUR_ADD;
char *extension = mstrcpy("");
char *extension = strdup("");
if (use_texture_rect)
mstrextend(&extension, "#extension GL_ARB_texture_rectangle : require\n");
if (ps->o.glx_use_gpushader4) {
@ -568,48 +574,42 @@ glx_init_blur(session_t *ps) {
(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);
char *shader_str = ccalloc(len, char);
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 = XFIXED_TO_DOUBLE(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);
}
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 = XFIXED_TO_DOUBLE(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);
}
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);
log_error("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.");
log_error("Failed to create GLSL program.");
return false;
}
@ -617,7 +617,7 @@ glx_init_blur(session_t *ps) {
#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); \
log_error("Failed to get location of %d-th uniform '" name "'. Might be troublesome.", i); \
} \
}
@ -654,7 +654,7 @@ glx_load_prog_main(session_t *ps,
// Build program
pprogram->prog = glx_create_program_from_str(vshader_str, fshader_str);
if (!pprogram->prog) {
printf_errf("(): Failed to create GLSL program.");
log_error("Failed to create GLSL program.");
return false;
}
@ -662,7 +662,7 @@ glx_load_prog_main(session_t *ps,
#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."); \
log_error("Failed to get location of uniform '" name "'. Might be troublesome."); \
} \
}
P_GET_UNIFM_LOC("opacity", unifm_opacity);
@ -685,7 +685,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
return true;
if (!pixmap) {
printf_errf("(%#010x): Binding to an empty pixmap. This can't work.", pixmap);
log_error("Binding to an empty pixmap %#010x. This can't work.", pixmap);
return false;
}
@ -705,7 +705,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
.y_inverted = false,
};
ptex = malloc(sizeof(glx_texture_t));
ptex = cmalloc(glx_texture_t);
allocchk(ptex);
memcpy(ptex, &GLX_TEX_DEF, sizeof(glx_texture_t));
*pptex = ptex;
@ -727,19 +727,19 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
unsigned rbdwid = 0;
if (!XGetGeometry(ps->dpy, pixmap, &rroot, &rx, &ry,
&width, &height, &rbdwid, &depth)) {
printf_errf("(%#010x): Failed to query Pixmap info.", pixmap);
log_error("Failed to query info of pixmap %#010x.", pixmap);
return false;
}
if (depth > OPENGL_MAX_DEPTH) {
printf_errf("(%d): Requested depth higher than %d.", depth,
OPENGL_MAX_DEPTH);
log_error("Requested depth %d 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);
log_error("Couldn't find FBConfig with requested depth %d.", depth);
return false;
}
@ -757,10 +757,8 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
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
log_debug("depth %d, tgt %#x, rgba %d", depth, tex_tgt,
(GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt));
GLint attrs[] = {
GLX_TEXTURE_FORMAT_EXT,
@ -780,7 +778,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
ptex->y_inverted = pcfg->y_inverted;
}
if (!ptex->glpixmap) {
printf_errf("(): Failed to allocate GLX pixmap.");
log_error("Failed to allocate GLX pixmap.");
return false;
}
@ -804,7 +802,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
ptex->texture = texture;
}
if (!ptex->texture) {
printf_errf("(): Failed to allocate texture.");
log_error("Failed to allocate texture.");
return false;
}
@ -1000,9 +998,7 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
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
//log_trace("%d, %d, %d, %d", mdx, mdy, mwidth, mheight);
/*
if (ps->o.resize_damage > 0) {
@ -1047,11 +1043,11 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
const GLuint fbo = pbc->fbo;
if (!tex_scr || (more_passes && !tex_scr2)) {
printf_errf("(): Failed to allocate texture.");
log_error("Failed to allocate texture.");
goto glx_blur_dst_end;
}
if (more_passes && !fbo) {
printf_errf("(): Failed to allocate framebuffer.");
log_error("Failed to allocate framebuffer.");
goto glx_blur_dst_end;
}
@ -1101,7 +1097,7 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
glDrawBuffers(1, DRAWBUFS);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER)
!= GL_FRAMEBUFFER_COMPLETE) {
printf_errf("(): Framebuffer attachment failed.");
log_error("Framebuffer attachment failed.");
goto glx_blur_dst_end;
}
}
@ -1144,9 +1140,8 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat rdxe = rdx + (crect.x2 - crect.x1);
GLfloat rdye = rdy - (crect.y2 - crect.y1);
#ifdef DEBUG_GLX
printf_dbgf("(): %f, %f, %f, %f -> %f, %f, %f, %f\n", rx, ry, rxe, rye, rdx, rdy, rdxe, rdye);
#endif
//log_trace("%f, %f, %f, %f -> %f, %f, %f, %f", rx, ry, rxe, rye, rdx,
// rdy, rdxe, rdye);
glTexCoord2f(rx, ry);
glVertex3f(rdx, rdy, z);
@ -1237,7 +1232,7 @@ glx_render(session_t *ps, const glx_texture_t *ptex,
const region_t *reg_tgt, const glx_prog_main_t *pprogram
) {
if (!ptex || !ptex->texture) {
printf_errf("(): Missing texture.");
log_error("Missing texture.");
return false;
}
@ -1351,9 +1346,8 @@ glx_render(session_t *ps, const glx_texture_t *ptex,
glUniform1i(pprogram->unifm_tex, 0);
}
#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
//log_trace("Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d", x, y, width, height,
// dx, dy, ptex->width, ptex->height, z);
// Bind texture
glBindTexture(ptex->target, ptex->texture);
@ -1391,9 +1385,8 @@ glx_render(session_t *ps, const glx_texture_t *ptex,
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
//log_trace("Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d", ri, rx, ry, rxe, rye,
// rdx, rdy, rdxe, rdye);
#define P_TEXCOORD(cx, cy) { \
if (dual_texture) { \
@ -1454,7 +1447,7 @@ glx_take_screenshot(session_t *ps, int *out_length) {
glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_align_old);
assert(unpack_align_old > 0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
unsigned char *buf = cmalloc(length, unsigned char);
auto buf = ccalloc(length, unsigned char);
glReadBuffer(GL_FRONT);
glReadPixels(0, 0, ps->root_width, ps->root_height, GL_RGB,
GL_UNSIGNED_BYTE, buf);
@ -1467,15 +1460,12 @@ glx_take_screenshot(session_t *ps, int *out_length) {
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
log_trace("glx_create_shader(): ===\n%s\n===", shader_str);
bool success = false;
GLuint shader = glCreateShader(shader_type);
if (!shader) {
printf_errf("(): Failed to create shader with type %#x.", shader_type);
log_error("Failed to create shader with type %#x.", shader_type);
goto glx_create_shader_end;
}
glShaderSource(shader, 1, &shader_str, NULL);
@ -1491,8 +1481,7 @@ glx_create_shader(GLenum shader_type, const char *shader_str) {
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);
log_error("Failed to compile shader with type %d: %s", shader_type, log);
}
goto glx_create_shader_end;
}
@ -1514,7 +1503,7 @@ glx_create_program(const GLuint * const shaders, int nshaders) {
bool success = false;
GLuint program = glCreateProgram();
if (!program) {
printf_errf("(): Failed to create program.");
log_error("Failed to create program.");
goto glx_create_program_end;
}
@ -1532,7 +1521,7 @@ glx_create_program(const GLuint * const shaders, int nshaders) {
if (log_len) {
char log[log_len + 1];
glGetProgramInfoLog(program, log_len, NULL, log);
printf_errf("(): Failed to link program: %s", log);
log_error("Failed to link program: %s", log);
}
goto glx_create_program_end;
}
@ -1567,17 +1556,15 @@ glx_create_program_from_str(const char *vert_shader_str,
if (frag_shader_str)
frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, frag_shader_str);
{
GLuint shaders[2];
unsigned 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);
}
GLuint shaders[2];
unsigned 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);

View file

@ -12,6 +12,7 @@
#pragma once
#include "common.h"
#include "log.h"
#include <ctype.h>
#include <locale.h>
@ -49,14 +50,14 @@ glx_check_err_(session_t *ps, const char *func, int line) {
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);
log_printf(tls_logger, LOG_LEVEL_ERROR, func, "GLX error at line %d: %s", line,
errtext);
}
else {
printf_dbg("%d\n", err);
log_printf(tls_logger, LOG_LEVEL_ERROR, func, "GLX error at line %d: %d", line,
err);
}
}
}
@ -95,13 +96,13 @@ 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.");
log_error("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);
log_info("Missing GLX extension %s.", ext);
return found;
}
@ -113,13 +114,13 @@ 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.");
log_error("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);
log_info("Missing GL extension %s.", ext);
return found;
}
@ -165,7 +166,7 @@ void
glx_release_pixmap(session_t *ps, glx_texture_t *ptex);
void glx_paint_pre(session_t *ps, region_t *preg)
__attribute__((nonnull(1, 2)));
attr_nonnull(1, 2);
/**
* Check if a texture is binded, or is binded to the given pixmap.
@ -198,6 +199,70 @@ glx_create_program_from_str(const char *vert_shader_str,
unsigned char *
glx_take_screenshot(session_t *ps, int *out_length);
/**
* Check if there's a GLX context.
*/
static inline bool
glx_has_context(session_t *ps) {
return ps->psglx && ps->psglx->context;
}
/**
* 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;
}
/**
* 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) {
if (*pfbo) {
glDeleteFramebuffers(1, pfbo);
*pfbo = 0;
}
assert(!*pfbo);
}
/**
* 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);
}
/**
* Free a glx_texture_t.
*/

862
src/options.c Normal file
View file

@ -0,0 +1,862 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#include <getopt.h>
#include <locale.h>
#include <stdbool.h>
#include <unistd.h>
#include "common.h"
#include "config.h"
#include "options.h"
#pragma GCC diagnostic error "-Wunused-parameter"
/**
* Print usage text and exit.
*/
static void usage(int ret) {
#define WARNING_DISABLED " (DISABLED AT COMPILE TIME)"
#define WARNING
static const char *usage_text =
"compton (" COMPTON_VERSION ")\n"
"This is the maintenance fork of compton, please report\n"
"bugs to https://github.com/yshui/compton\n\n"
"usage: compton [options]\n"
"Options:\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"
"--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_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 = Enable driver-level VSync. Works only with GLX "
"backend." WARNING "\n"
" opengl-mswc = Deprecated, use opengl-swc instead." 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"
"--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_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-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. \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
#define WARNING
"\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"
"--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"
"--monitor-repaint\n"
" Highlight the updated area of the screen. For debugging the xrender\n"
" backend only.\n";
FILE *f = (ret ? stderr : stdout);
fputs(usage_text, f);
#undef WARNING
#undef WARNING_DISABLED
}
static const char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:hscnfFCaSzGb";
static const 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},
{"log-level", required_argument, NULL, 321},
{"log-file", required_argument, NULL, 322},
{"reredir-on-root-change", no_argument, NULL, 731},
{"glx-reinit-on-root-change", no_argument, NULL, 732},
{"monitor-repaint", no_argument, NULL, 800},
{"diagnostics", no_argument, NULL, 801},
// Must terminate with a NULL entry
{NULL, 0, NULL, 0},
};
/// Get config options that are needed to parse the rest of the options
/// Return true if we should quit
bool get_early_config(int argc, char *const *argv, char **config_file, bool *all_xerrors,
int *exit_code) {
int o = 0, longopt_idx = -1;
// 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;
*config_file = NULL;
while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) {
if (o == 256) {
*config_file = strdup(optarg);
} else if (o == 'd') {
log_warn("-d will be ignored, please use the DISPLAY "
"environment variable");
} else if (o == 314) {
*all_xerrors = true;
} else if (o == 318) {
printf("%s\n", COMPTON_VERSION);
*exit_code = 0;
return true;
} else if (o == 'S') {
log_warn("-S will be ignored");
} else if (o == 320) {
log_warn("--no-name-pixmap will be ignored");
} else if (o == '?' || o == ':') {
usage(1);
*exit_code = 1;
return true;
}
}
// Check for abundant positional arguments
if (optind < argc)
log_fatal("compton doesn't accept positional arguments.");
return false;
}
/**
* Process arguments and configuration files.
*/
void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
bool fading_enable, bool conv_kern_hasneg, win_option_mask_t *winopt_mask) {
int o = 0, longopt_idx = -1;
char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL));
// Enforce LC_NUMERIC locale "C" here to make sure dots are recognized
// instead of commas in atof().
setlocale(LC_NUMERIC, "C");
// Parse commandline arguments. Range checking will be done later.
const char *deprecation_message = "has been removed. If you encounter problems "
"without this feature, please feel free to "
"open a bug report.";
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: \
opt->option = true; \
break
#define P_CASELONG(idx, option) \
case idx: \
if (!parse_long(optarg, &val)) \
exit(1); \
opt->option = val; \
break
// clang-format off
// 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': opt->fade_in_step = normalize_d(atof(optarg)) * OPAQUE; break;
case 'O': opt->fade_out_step = normalize_d(atof(optarg)) * OPAQUE; break;
case 'c': shadow_enable = true; break;
case 'C':
winopt_mask[WINTYPE_DOCK].shadow = true;
opt->wintype_option[WINTYPE_DOCK].shadow = true;
break;
case 'G':
winopt_mask[WINTYPE_DND].shadow = true;
opt->wintype_option[WINTYPE_DND].shadow = true;
break;
case 'm':;
double tmp;
tmp = normalize_d(atof(optarg));
winopt_mask[WINTYPE_DROPDOWN_MENU].opacity = true;
winopt_mask[WINTYPE_POPUP_MENU].opacity = true;
opt->wintype_option[WINTYPE_POPUP_MENU].opacity = tmp;
opt->wintype_option[WINTYPE_DROPDOWN_MENU].opacity = tmp;
break;
case 'f':
case 'F':
fading_enable = true;
break;
P_CASELONG('r', shadow_radius);
case 'o':
opt->shadow_opacity = atof(optarg);
break;
P_CASELONG('l', shadow_offset_x);
P_CASELONG('t', shadow_offset_y);
case 'i':
opt->inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE);
break;
case 'e': opt->frame_opacity = atof(optarg); break;
case 'z':
log_warn("clear-shadow is removed, shadows are automatically "
"cleared now. If you want to prevent shadow from been "
"cleared under certain types of windows, you can use "
"the \"full-shadow\" per window type option.");
break;
case 'n':
case 'a':
case 's':
log_error("-n, -a, and -s have been removed.");
break;
P_CASEBOOL('b', fork_after_register);
// Long options
case 256:
// --config
break;
case 257:
// --shadow-red
opt->shadow_red = atof(optarg);
break;
case 258:
// --shadow-green
opt->shadow_green = atof(optarg);
break;
case 259:
// --shadow-blue
opt->shadow_blue = atof(optarg);
break;
P_CASEBOOL(260, inactive_opacity_override);
case 261:
// --inactive-dim
opt->inactive_dim = atof(optarg);
break;
P_CASEBOOL(262, mark_wmwin_focused);
case 263:
// --shadow-exclude
condlst_add(&opt->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
opt->vsync = parse_vsync(optarg);
if (opt->vsync >= NUM_VSYNC)
exit(1);
break;
case 271:
// --alpha-step
log_warn("--alpha-step has been removed, compton now tries to "
"make use of all alpha values");
break;
case 272: log_warn("use of --dbe is deprecated"); break;
case 273:
log_warn("--paint-on-overlay has been removed, and is enabled "
"when possible");
break;
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(&opt->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:
log_warn("Please use --log-file instead of --logpath");
// fallthrough
case 322:
// --logpath, --log-file
free(opt->logpath);
opt->logpath = strdup(optarg);
break;
case 288:
// --invert-color-include
condlst_add(&opt->invert_color_list, optarg);
break;
case 289:
// --opengl
opt->backend = BKEND_GLX;
break;
case 290:
// --backend
opt->backend = parse_backend(optarg);
if (opt->backend >= NUM_BKEND)
exit(1);
break;
P_CASEBOOL(291, glx_no_stencil);
case 292:
log_warn("--glx-copy-from-front %s", deprecation_message);
break;
P_CASELONG(293, benchmark);
case 294:
// --benchmark-wid
opt->benchmark_wid = strtol(optarg, NULL, 0);
break;
case 295:
log_warn("--glx-use-copysubbuffermesa %s", deprecation_message);
break;
case 296:
// --blur-background-exclude
condlst_add(&opt->blur_background_blacklist, optarg);
break;
case 297:
// --active-opacity
opt->active_opacity = (normalize_d(atof(optarg)) * OPAQUE);
break;
P_CASEBOOL(298, glx_no_rebind_pixmap);
case 299:
// --glx-swap-method
opt->glx_swap_method = parse_glx_swap_method(optarg);
if (opt->glx_swap_method == -2)
exit(1);
break;
case 300:
// --fade-exclude
condlst_add(&opt->fade_blacklist, optarg);
break;
case 301:
// --blur-kern
if (!parse_conv_kern_lst(optarg, opt->blur_kerns,
MAX_BLUR_PASS, &conv_kern_hasneg))
exit(1);
break;
P_CASELONG(302, resize_damage);
P_CASEBOOL(303, glx_use_gpushader4);
case 304:
// --opacity-rule
if (!parse_rule_opacity(&opt->opacity_rules, optarg))
exit(1);
break;
case 305:
// --shadow-exclude-reg
free(opt->shadow_exclude_reg_str);
opt->shadow_exclude_reg_str = strdup(optarg);
log_warn("--shadow-exclude-reg is deprecated. You are likely "
"better off using --shadow-exclude anyway");
break;
case 306:
// --paint-exclude
condlst_add(&opt->paint_blacklist, optarg);
break;
P_CASEBOOL(307, xinerama_shadow_crop);
case 308:
// --unredir-if-possible-exclude
condlst_add(&opt->unredir_if_possible_blacklist, optarg);
break;
P_CASELONG(309, unredir_if_possible_delay);
case 310:
// --write-pid-path
opt->write_pid_path = strdup(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:
opt->glx_fshader_win_str = strdup(optarg);
log_warn("--glx-fshader-win is being deprecated, and might be "
"removed in the future. If you really need this "
"feature, please report an issue to let us know");
break;
case 321: {
enum log_level tmp_level = string_to_log_level(optarg);
if (tmp_level == LOG_LEVEL_INVALID) {
log_warn("Invalid log level, defaults to WARN");
} else {
log_set_level_tls(tmp_level);
}
break;
}
P_CASEBOOL(319, no_x_selection);
P_CASEBOOL(731, reredir_on_root_change);
P_CASEBOOL(732, glx_reinit_on_root_change);
P_CASEBOOL(800, monitor_repaint);
case 801: opt->print_diagnostics = true; break;
default: usage(1); break;
#undef P_CASEBOOL
}
// clang-format on
}
// Restore LC_NUMERIC
setlocale(LC_NUMERIC, lc_numeric_old);
free(lc_numeric_old);
if (opt->monitor_repaint && opt->backend != BKEND_XRENDER) {
log_warn("--monitor-repaint has no effect when backend is not xrender");
}
// Range checking and option assignments
opt->fade_delta = max_i(opt->fade_delta, 1);
opt->shadow_radius = max_i(opt->shadow_radius, 0);
opt->shadow_red = normalize_d(opt->shadow_red);
opt->shadow_green = normalize_d(opt->shadow_green);
opt->shadow_blue = normalize_d(opt->shadow_blue);
opt->inactive_dim = normalize_d(opt->inactive_dim);
opt->frame_opacity = normalize_d(opt->frame_opacity);
opt->shadow_opacity = normalize_d(opt->shadow_opacity);
opt->refresh_rate = normalize_i_range(opt->refresh_rate, 0, 300);
// Apply default wintype options that are dependent on global options
for (int i = 0; i < NUM_WINTYPES; i++) {
auto wo = &opt->wintype_option[i];
auto mask = &winopt_mask[i];
if (!mask->shadow) {
wo->shadow = shadow_enable;
mask->shadow = true;
}
if (!mask->fade) {
wo->fade = fading_enable;
mask->fade = true;
}
}
// --blur-background-frame implies --blur-background
if (opt->blur_background_frame)
opt->blur_background = true;
if (opt->xrender_sync_fence)
opt->xrender_sync = true;
// Other variables determined by options
// Determine whether we need to track focus changes
if (opt->inactive_opacity != opt->active_opacity || opt->inactive_dim) {
opt->track_focus = true;
}
// Determine whether we track window grouping
if (opt->detect_transient || opt->detect_client_leader) {
opt->track_leader = true;
}
// Fill default blur kernel
if (opt->blur_background && !opt->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
static const xcb_render_fixed_t convolution_blur[] = {
// Must convert to XFixed with DOUBLE_TO_XFIXED()
// Matrix size
DOUBLE_TO_XFIXED(3),
DOUBLE_TO_XFIXED(3),
// Matrix
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
};
opt->blur_kerns[0] =
ccalloc(ARR_SIZE(convolution_blur), xcb_render_fixed_t);
memcpy(opt->blur_kerns[0], convolution_blur, sizeof(convolution_blur));
}
if (opt->resize_damage < 0)
log_warn("Negative --resize-damage will not work correctly.");
if (opt->backend == BKEND_XRENDER && conv_kern_hasneg)
log_warn("A convolution kernel with negative values may not work "
"properly under X Render backend.");
}
// vim: set noet sw=8 ts=8 :

34
src/options.h Normal file
View file

@ -0,0 +1,34 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
/// Parse command line options
#include <stdbool.h>
#include <xcb/render.h> // for xcb_render_fixed_t
#include "compiler.h"
#include "config.h"
#include "types.h"
#include "win.h" // for wintype_t
typedef struct session session_t;
/// Get config options that are needed to parse the rest of the options
/// Return true if we should quit
bool get_early_config(int argc, char *const *argv, char **config_file, bool *all_xerrors,
int *exit_code);
/**
* Process arguments and configuration files.
*
* Parameters:
* shadow_enable = Carry overs from parse_config
* fading_enable
* conv_kern_hasneg
* winopt_mask
*/
void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
bool fading_enable, bool conv_kern_hasneg, win_option_mask_t *winopt_mask);
// vim: set noet sw=8 ts=8:

View file

@ -4,7 +4,9 @@
#include <xcb/xcb.h>
#include <pixman.h>
#include <stdio.h>
#include "utils.h"
#include "log.h"
typedef struct pixman_region32 pixman_region32_t;
typedef struct pixman_box32 pixman_box32_t;
@ -23,9 +25,9 @@ static inline void
dump_region(const region_t *x) {
int nrects;
const rect_t *rects = pixman_region32_rectangles((region_t *)x, &nrects);
fprintf(stderr, "nrects: %d\n", nrects);
log_trace("nrects: %d", nrects);
for (int i = 0; i < nrects; i++)
fprintf(stderr, "(%d, %d) - (%d, %d)\n", rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2);
log_trace("(%d, %d) - (%d, %d)", rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2);
}
/// Convert one xcb rectangle to our rectangle type
@ -43,7 +45,7 @@ from_x_rect(const xcb_rectangle_t *rect) {
/// Returning an array that needs to be freed
static inline rect_t *
from_x_rects(int nrects, const xcb_rectangle_t *rects) {
rect_t *ret = calloc(nrects, sizeof *ret);
rect_t *ret = ccalloc(nrects, rect_t);
for (int i = 0; i < nrects; i++)
ret[i] = from_x_rect(rects+i);
return ret;

1333
src/render.c Normal file

File diff suppressed because it is too large Load diff

39
src/render.h Normal file
View file

@ -0,0 +1,39 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <xcb/render.h>
#include <xcb/xcb_image.h>
#include "region.h"
typedef struct _glx_texture glx_texture_t;
typedef struct glx_prog_main glx_prog_main_t;
typedef struct win win;
typedef struct session session_t;
typedef struct paint {
xcb_pixmap_t pixmap;
xcb_render_picture_t pict;
glx_texture_t *ptex;
} paint_t;
void
render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
double opacity, bool argb, bool neg,
xcb_render_picture_t pict, glx_texture_t *ptex,
const region_t *reg_paint, const glx_prog_main_t *pprogram);
void
paint_one(session_t *ps, win *w, const region_t *reg_paint);
void
paint_all(session_t *ps, region_t *region, const region_t *region_real, win * const t);
void free_picture(xcb_connection_t *c, xcb_render_picture_t *p);
void free_paint(session_t *ps, paint_t *ppaint);
void free_root_tile(session_t *ps);
bool init_render(session_t *ps);
void deinit_render(session_t *ps);
xcb_image_t *make_shadow(session_t *ps, double opacity, int width, int height);

43
src/string_utils.c Normal file
View file

@ -0,0 +1,43 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#include <string.h>
#include "compiler.h"
#include "string_utils.h"
#include "utils.h"
/**
* Allocate the space and join two strings.
*/
char *mstrjoin(const char *src1, const char *src2) {
auto str = ccalloc(strlen(src1)+strlen(src2)+1, char);
strcpy(str, src1);
strcat(str, src2);
return str;
}
/**
* Allocate the space and join two strings;
*/
char *
mstrjoin3(const char *src1, const char *src2, const char *src3) {
auto str = ccalloc(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.
*/
void mstrextend(char **psrc1, const char *src2) {
*psrc1 = crealloc(*psrc1, (*psrc1 ? strlen(*psrc1) : 0)+strlen(src2)+1);
strcat(*psrc1, src2);
}

54
src/string_utils.h Normal file
View file

@ -0,0 +1,54 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <ctype.h>
#include <stddef.h>
#define mstrncmp(s1, s2) strncmp((s1), (s2), strlen(s1))
char *mstrjoin(const char *src1, const char *src2);
char *
mstrjoin3(const char *src1, const char *src2, const char *src3);
void mstrextend(char **psrc1, const char *src2);
static inline int uitostr(unsigned int n, char *buf) {
int ret = 0;
unsigned int tmp = n;
while (tmp > 0) {
tmp /= 10;
ret++;
}
if (ret == 0)
ret = 1;
int pos = ret;
while (pos--) {
buf[pos] = n%10 + '0';
n /= 10;
}
return ret;
}
static inline const char *
skip_space_const(const char *src) {
if (!src)
return NULL;
while (*src && isspace(*src))
src++;
return src;
}
static inline char *
skip_space_mut(char *src) {
if (!src)
return NULL;
while (*src && isspace(*src))
src++;
return src;
}
#define skip_space(x) _Generic((x), \
char *: skip_space_mut, \
const char *: skip_space_const \
)(x)

34
src/utils.c Normal file
View file

@ -0,0 +1,34 @@
#include <stdio.h>
#include <sys/uio.h>
#include "compiler.h"
#include "string_utils.h"
#include "utils.h"
/// Report allocation failure without allocating memory
void report_allocation_failure(const char *func, const char *file, unsigned int line) {
// Since memory allocation failed, we try to print this error message without any
// memory allocation. Since logging framework allocates memory (and might even
// have not been initialized yet), so we can't use it.
char buf[11];
int llen = uitostr(line, buf);
const char msg1[] = " has failed to allocate memory, ";
const char msg2[] = ". Aborting...\n";
const struct iovec v[] = {
{.iov_base = (void *)func, .iov_len = strlen(func)},
{.iov_base = "()", .iov_len = 2},
{.iov_base = (void *)msg1, .iov_len = sizeof(msg1) - 1},
{.iov_base = "at ", .iov_len = 3},
{.iov_base = (void *)file, .iov_len = strlen(file)},
{.iov_base = ":", .iov_len = 1},
{.iov_base = buf, .iov_len = llen},
{.iov_base = (void *)msg2, .iov_len = sizeof(msg2) - 1},
};
writev(STDERR_FILENO, v, ARR_SIZE(v));
abort();
unreachable;
}
// vim: set noet sw=8 ts=8 :

View file

@ -5,7 +5,14 @@
#include <stddef.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <unistd.h>
#include "compiler.h"
#define ARR_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
#ifdef __FAST_MATH__
#warning Use of -ffast-math can cause rendering error or artifacts, \
@ -29,7 +36,7 @@ static inline bool safe_isnan(double a) {
* @param max maximum value
* @return normalized value
*/
static inline int __attribute__((const))
static inline int attr_const
normalize_i_range(int i, int min, int max) {
if (i > max) return max;
if (i < min) return min;
@ -39,7 +46,7 @@ normalize_i_range(int i, int min, int max) {
/**
* Select the larger integer of two.
*/
static inline int __attribute__((const))
static inline int attr_const
max_i(int a, int b) {
return (a > b ? a : b);
}
@ -47,7 +54,7 @@ max_i(int a, int b) {
/**
* Select the smaller integer of two.
*/
static inline int __attribute__((const))
static inline int attr_const
min_i(int a, int b) {
return (a > b ? b : a);
}
@ -55,7 +62,7 @@ min_i(int a, int b) {
/**
* Select the larger long integer of two.
*/
static inline long __attribute__((const))
static inline long attr_const
max_l(long a, long b) {
return (a > b ? a : b);
}
@ -63,7 +70,7 @@ max_l(long a, long b) {
/**
* Select the smaller long integer of two.
*/
static inline long __attribute__((const))
static inline long attr_const
min_l(long a, long b) {
return (a > b ? b : a);
}
@ -76,7 +83,7 @@ min_l(long a, long b) {
* @param max maximum value
* @return normalized value
*/
static inline double __attribute__((const))
static inline double attr_const
normalize_d_range(double d, double min, double max) {
if (d > max) return max;
if (d < min) return min;
@ -89,33 +96,39 @@ normalize_d_range(double d, double min, double max) {
* @param d double value to normalize
* @return normalized value
*/
static inline double __attribute__((const))
static inline double attr_const
normalize_d(double d) {
return normalize_d_range(d, 0.0, 1.0);
}
static inline const char *
skip_space_const(const char *src) {
if (!src)
return NULL;
while (*src && isspace(*src))
src++;
return src;
void report_allocation_failure(const char *func, const char *file, unsigned int line);
/**
* @brief Quit if the passed-in pointer is empty.
*/
static inline void *
allocchk_(const char *func_name, const char *file, unsigned int line, void *ptr) {
if (unlikely(!ptr)) {
report_allocation_failure(func_name, file, line);
}
return ptr;
}
static inline char *
skip_space_mut(char *src) {
if (!src)
return NULL;
while (*src && isspace(*src))
src++;
return src;
}
/// @brief Wrapper of allocchk_().
#define allocchk(ptr) allocchk_(__func__, __FILE__, __LINE__, ptr)
#define skip_space(x) _Generic((x), \
char *: skip_space_mut, \
const char *: skip_space_const \
)(x)
/// @brief Wrapper of malloc().
#define cmalloc(type) ((type *) allocchk(malloc(sizeof(type))))
/// @brief Wrapper of malloc() that takes a size
#define cvalloc(size) allocchk(malloc(size))
/// @brief Wrapper of calloc().
#define ccalloc(nmemb, type) ((type *) allocchk(calloc((nmemb), sizeof(type))))
/// @brief Wrapper of ealloc().
#define crealloc(ptr, nmemb) \
((__typeof__(ptr)) allocchk(realloc((ptr), (nmemb) * sizeof(*(ptr)))))
/// RC_TYPE generates a reference counted type from `type`
///
@ -142,7 +155,7 @@ typedef struct { \
} name##_internal_t; \
typedef type name##_t; \
Q type *name##_new(void) { \
name##_internal_t *ret = malloc(sizeof(name##_internal_t)); \
name##_internal_t *ret = cmalloc(name##_internal_t); \
ctor((type *)ret); \
ret->ref_count = 1; \
return (type *)ret; \

301
src/vsync.c Normal file
View file

@ -0,0 +1,301 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
/// Function pointers to init VSync modes.
#include "common.h"
#include "log.h"
#ifdef CONFIG_OPENGL
#include "opengl.h"
#endif
#ifdef CONFIG_VSYNC_DRM
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <drm.h>
#include <errno.h>
#include <sys/ioctl.h>
#endif
#include "vsync.h"
#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)
log_error("VBlank ioctl did not work, unimplemented in this drmver?");
return ret;
}
#endif
/**
* 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) {
log_error("Failed to open device.");
return false;
}
if (vsync_drm_wait(ps))
return false;
return true;
#else
log_error("compton is not compiled with DRM VSync support.");
return false;
#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_OPENGL
if (!ensure_glx_context(ps))
return false;
if (!glx_hasglxext(ps, "GLX_SGI_video_sync")) {
log_error("Your driver doesn't support SGI_video_sync, giving up.");
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) {
log_error("Failed to get glXWait/GetVideoSyncSGI function.");
return false;
}
return true;
#else
log_error("compton is not compiled with OpenGL VSync support.");
return false;
#endif
}
static bool
vsync_opengl_oml_init(session_t *ps) {
#ifdef CONFIG_OPENGL
if (!ensure_glx_context(ps))
return false;
if (!glx_hasglxext(ps, "GLX_OML_sync_control")) {
log_error("Your driver doesn't support OML_sync_control, giving up.");
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) {
log_error("Failed to get OML_sync_control functions.");
return false;
}
return true;
#else
log_error("compton is not compiled with OpenGL VSync support.");
return false;
#endif
}
static bool
vsync_opengl_swc_swap_interval(session_t *ps, unsigned int interval) {
#ifdef CONFIG_OPENGL
if (!ensure_glx_context(ps))
return false;
if (!ps->psglx->glXSwapIntervalProc && !ps->psglx->glXSwapIntervalMESAProc) {
if (glx_hasglxext(ps, "GLX_MESA_swap_control")) {
ps->psglx->glXSwapIntervalMESAProc = (f_SwapIntervalMESA)
glXGetProcAddress ((const GLubyte *) "glXSwapIntervalMESA");
} else if (glx_hasglxext(ps, "GLX_SGI_swap_control")) {
ps->psglx->glXSwapIntervalProc = (f_SwapIntervalSGI)
glXGetProcAddress ((const GLubyte *) "glXSwapIntervalSGI");
} else {
log_error("Your driver doesn't support SGI_swap_control nor MESA_swap_control, giving up.");
return false;
}
}
if (ps->psglx->glXSwapIntervalMESAProc)
ps->psglx->glXSwapIntervalMESAProc(interval);
else if (ps->psglx->glXSwapIntervalProc)
ps->psglx->glXSwapIntervalProc(interval);
else
return false;
#endif
return true;
}
static bool
vsync_opengl_swc_init(session_t *ps) {
#ifdef CONFIG_OPENGL
if (!bkend_use_glx(ps)) {
log_warn("OpenGL swap control requires the GLX backend.");
return false;
}
if (!vsync_opengl_swc_swap_interval(ps, 1)) {
log_error("Failed to load a swap control extension.");
return false;
}
return true;
#else
log_error("compton is not compiled with OpenGL VSync support.");
return false;
#endif
}
static bool
vsync_opengl_mswc_init(session_t *ps) {
log_warn("opengl-mswc is deprecated, please use opengl-swc instead.");
return vsync_opengl_swc_init(ps);
}
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,
};
#ifdef CONFIG_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;
}
#endif
/// Function pointers to wait for VSync.
int (*const VSYNC_FUNCS_WAIT[NUM_VSYNC])(session_t *ps) = {
#ifdef CONFIG_VSYNC_DRM
[VSYNC_DRM ] = vsync_drm_wait,
#endif
#ifdef CONFIG_OPENGL
[VSYNC_OPENGL ] = vsync_opengl_wait,
[VSYNC_OPENGL_OML ] = vsync_opengl_oml_wait,
#endif
};
#ifdef CONFIG_OPENGL
static void
vsync_opengl_swc_deinit(session_t *ps) {
vsync_opengl_swc_swap_interval(ps, 0);
}
#endif
/// Function pointers to deinitialize VSync.
void (*const VSYNC_FUNCS_DEINIT[NUM_VSYNC])(session_t *ps) = {
#ifdef CONFIG_OPENGL
[VSYNC_OPENGL_SWC ] = vsync_opengl_swc_deinit,
[VSYNC_OPENGL_MSWC ] = vsync_opengl_swc_deinit,
#endif
};
/**
* Initialize current VSync method.
*/
bool vsync_init(session_t *ps) {
// Mesa turns on swap control by default, undo that
if (bkend_use_glx(ps))
vsync_opengl_swc_swap_interval(ps, 0);
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.
*/
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);
}

9
src/vsync.h Normal file
View file

@ -0,0 +1,9 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#include <stdbool.h>
typedef struct session session_t;
bool vsync_init(session_t *ps);
void vsync_wait(session_t *ps);
void vsync_deinit(session_t *ps);

120
src/win.c
View file

@ -8,10 +8,18 @@
#include <stdbool.h>
#include <math.h>
#include "compiler.h"
#include "common.h"
#include "compton.h"
#include "c2.h"
#include "x.h"
#include "string_utils.h"
#include "utils.h"
#include "log.h"
#ifdef CONFIG_DBUS
#include "dbus.h"
#endif
#include "win.h"
@ -109,6 +117,8 @@ void win_get_region_noframe_local(win *w, region_t *res) {
pixman_region32_init_rect(res, x, y, width, height);
}
gen_by_val(win_get_region_noframe_local)
/**
* Add a window to damaged area.
*
@ -168,9 +178,7 @@ int win_get_name(session_t *ps, win *w) {
return 0;
if (!(wid_get_text_prop(ps, w->client_win, ps->atom_name_ewmh, &strlst, &nstr))) {
#ifdef DEBUG_WINDATA
printf_dbgf("(%#010lx): _NET_WM_NAME unset, falling back to WM_NAME.\n", wid);
#endif
log_trace("(%#010lx): _NET_WM_NAME unset, falling back to WM_NAME.", w->client_win);
if (!(XGetWMName(ps->dpy, w->client_win, &text_prop) && text_prop.value)) {
return -1;
@ -190,15 +198,13 @@ int win_get_name(session_t *ps, win *w) {
if (!w->name || strcmp(w->name, strlst[0]) != 0) {
ret = 1;
free(w->name);
w->name = mstrcpy(strlst[0]);
w->name = strdup(strlst[0]);
}
XFreeStringList(strlst);
#ifdef DEBUG_WINDATA
printf_dbgf("(%#010lx): client = %#010lx, name = \"%s\", "
"ret = %d\n", w->id, w->client_win, w->name, ret);
#endif
log_trace("(%#010lx): client = %#010lx, name = \"%s\", "
"ret = %d", w->id, w->client_win, w->name, ret);
return ret;
}
@ -213,15 +219,13 @@ int win_get_role(session_t *ps, win *w) {
if (!w->role || strcmp(w->role, strlst[0]) != 0) {
ret = 1;
free(w->role);
w->role = mstrcpy(strlst[0]);
w->role = strdup(strlst[0]);
}
XFreeStringList(strlst);
#ifdef DEBUG_WINDATA
printf_dbgf("(%#010lx): client = %#010lx, role = \"%s\", "
"ret = %d\n", w->id, w->client_win, w->role, ret);
#endif
log_trace("(%#010lx): client = %#010lx, role = \"%s\", "
"ret = %d", w->id, w->client_win, w->role, ret);
return ret;
}
@ -323,8 +327,8 @@ void win_calc_opacity(session_t *ps, win *w) {
// Try obeying opacity property and window type opacity firstly
if (w->has_opacity_prop)
opacity = w->opacity_prop;
else if (!safe_isnan(ps->o.wintype_opacity[w->window_type]))
opacity = ps->o.wintype_opacity[w->window_type] * OPAQUE;
else if (!safe_isnan(ps->o.wintype_option[w->window_type].opacity))
opacity = ps->o.wintype_option[w->window_type].opacity * OPAQUE;
else {
// Respect active_opacity only when the window is physically focused
if (win_is_focused_real(ps, w))
@ -384,7 +388,7 @@ void win_determine_fade(session_t *ps, win *w) {
} else if (c2_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst, NULL))
w->fade = false;
else
w->fade = ps->o.wintype_fade[w->window_type];
w->fade = ps->o.wintype_option[w->window_type].fade;
}
/**
@ -454,7 +458,7 @@ void win_determine_shadow(session_t *ps, win *w) {
if (UNSET != w->shadow_force)
shadow_new = w->shadow_force;
else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE)
shadow_new = (ps->o.wintype_shadow[w->window_type] &&
shadow_new = (ps->o.wintype_option[w->window_type].shadow &&
!c2_match(ps, w, ps->o.shadow_blacklist, &w->cache_sblst, NULL) &&
!(ps->o.shadow_ignore_shaped && w->bounding_shaped &&
!w->rounded_corners) &&
@ -699,19 +703,15 @@ void win_recheck_client(session_t *ps, win *w) {
// 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
log_trace("(%#010lx): client %#010lx", w->id, cw);
// 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,
log_trace("(%#010lx): client self (%s)", w->id,
(w->wmwin ? "wmwin" : "override-redirected"));
#endif
}
// Unmark the old one
@ -725,6 +725,7 @@ void win_recheck_client(session_t *ps, win *w) {
// TODO: probably split into win_new (in win.c) and add_win (in compton.c)
bool add_win(session_t *ps, Window id, Window prev) {
static const win win_def = {
.win_data = NULL,
.next = NULL,
.prev_trans = NULL,
@ -811,16 +812,9 @@ bool add_win(session_t *ps, Window id, Window prev) {
}
// Allocate and initialize the new win structure
win *new = malloc(sizeof(win));
auto new = cmalloc(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;
}
log_trace("(%#010lx): %p", id, new);
*new = win_def;
pixman_region32_init(&new->bounding_shape);
@ -914,7 +908,7 @@ void win_update_focused(session_t *ps, win *w) {
// Use wintype_focus, and treat WM windows and override-redirected
// windows specially
if (ps->o.wintype_focus[w->window_type]
if (ps->o.wintype_option[w->window_type].focus
|| (ps->o.mark_wmwin_focused && w->wmwin)
|| (ps->o.mark_ovredir_focused &&
w->id == w->client_win && !w->wmwin)
@ -984,9 +978,8 @@ void win_update_leader(session_t *ps, win *w) {
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
log_trace("(%#010lx): client %#010lx, leader %#010lx, cache %#010lx",
w->id, w->client_win, w->leader, win_get_leader(ps, w));
}
/**
@ -1038,18 +1031,16 @@ bool win_get_class(session_t *ps, win *w) {
return false;
// Copy the strings if successful
w->class_instance = mstrcpy(strlst[0]);
w->class_instance = strdup(strlst[0]);
if (nstr > 1)
w->class_general = mstrcpy(strlst[1]);
w->class_general = strdup(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
log_trace("(%#010lx): client = %#010lx, "
"instance = \"%s\", general = \"%s\"",
w->id, w->client_win, w->class_instance, w->class_general);
return true;
}
@ -1138,9 +1129,12 @@ void win_extents(win *w, region_t *res) {
pixman_region32_union_rect(res, res, w->g.x, w->g.y, w->widthb, w->heightb);
if (w->shadow)
pixman_region32_union_rect(res, res, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy, w->shadow_width, w->shadow_height);
pixman_region32_union_rect(res, res, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, w->shadow_width, w->shadow_height);
}
gen_by_val(win_extents)
/**
* Update the out-dated bounding shape of a window.
*
@ -1196,7 +1190,7 @@ void win_update_bounding_shape(session_t *ps, win *w) {
// Window shape changed, we should free old wpaint and shadow pict
free_paint(ps, &w->paint);
free_paint(ps, &w->shadow_paint);
//printf_errf("(): free out dated pict");
//log_trace("free out dated pict");
win_on_factor_change(ps, w);
}
@ -1247,11 +1241,9 @@ win_update_frame_extents(session_t *ps, win *w, Window client) {
w->reg_ignore_valid = false;
}
#ifdef DEBUG_FRAME
printf_dbgf("(%#010lx): %d, %d, %d, %d\n", w->id,
log_trace("(%#010lx): %d, %d, %d, %d", w->id,
w->frame_extents.left, w->frame_extents.right,
w->frame_extents.top, w->frame_extents.bottom);
#endif
free_winprop(&prop);
}
@ -1284,3 +1276,35 @@ void win_ev_stop(session_t *ps, win *w) {
xcb_shape_select_input(ps->c, w->id, 0));
}
}
/**
* Set fade callback of a window, and possibly execute the previous
* callback.
*
* If a callback can cause rendering result to change, it should call
* `queue_redraw`.
*
* @param exec_callback whether the previous callback is to be executed
*/
void win_set_fade_callback(session_t *ps, win **_w,
void (*callback) (session_t *ps, win **w), bool exec_callback) {
win *w = *_w;
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);
}
/**
* Execute fade callback of a window if fading finished.
*/
void
win_check_fade_finished(session_t *ps, win **_w) {
win *w = *_w;
if (w->fade_callback && w->opacity == w->opacity_tgt) {
// Must be the last line as the callback could destroy w!
win_set_fade_callback(ps, _w, NULL, true);
}
}

View file

@ -15,18 +15,11 @@
#include "x.h"
#include "types.h"
#include "c2.h"
#include "utils.h"
#include "render.h"
typedef struct session session_t;
typedef struct _glx_texture glx_texture_t;
// FIXME not the best place for this type
typedef struct {
xcb_pixmap_t pixmap;
xcb_render_picture_t pict;
glx_texture_t *ptex;
} paint_t;
#ifdef CONFIG_OPENGL
// FIXME this type should be in opengl.h
// it is very unideal for it to be here
@ -81,6 +74,9 @@ typedef enum {
/// Structure representing a top-level window compton manages.
typedef struct win win;
struct win {
/// backend data attached to this window. Only available when
/// `state` is not UNMAPPED
void *win_data;
/// Pointer to the next lower window in window stack.
win *next;
/// Pointer to the next higher window to paint.
@ -121,7 +117,8 @@ struct win {
bool need_configure;
/// Queued <code>ConfigureNotify</code> when the window is unmapped.
xcb_configure_notify_event_t queue_configure;
/// The region of screen that will be obscured when windows above is painted.
/// The region of screen that will be obscured when windows above is painted,
/// in global coordinates.
/// We use this to reduce the pixels that needed to be paint when painting
/// this window and anything underneath. Depends on window frame
/// opacity state, window geometry, window mapped/unmapped state,
@ -309,12 +306,14 @@ void win_update_focused(session_t *ps, win *w);
// XXX was win_border_size
void win_update_bounding_shape(session_t *ps, win *w);
/**
* Get a rectangular region a window (and possibly its shadow) occupies.
* Get a rectangular region in global coordinates a window (and possibly
* its shadow) occupies.
*
* Note w->shadow and shadow geometry must be correct before calling this
* function.
*/
void win_extents(win *w, region_t *res);
region_t win_extents_by_val(win *w);
/**
* Add a window to damaged area.
*
@ -328,6 +327,7 @@ void add_damage_from_win(session_t *ps, win *w);
* Return region in global coordinates.
*/
void win_get_region_noframe_local(win *w, region_t *);
region_t win_get_region_noframe_local_by_val(win *w);
/**
* Retrieve frame extents from a window.
*/
@ -335,6 +335,24 @@ void
win_update_frame_extents(session_t *ps, win *w, Window client);
bool add_win(session_t *ps, Window id, Window prev);
/**
* Set fade callback of a window, and possibly execute the previous
* callback.
*
* If a callback can cause rendering result to change, it should call
* `queue_redraw`.
*
* @param exec_callback whether the previous callback is to be executed
*/
void win_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.
*/
void
win_check_fade_finished(session_t *ps, win **_w);
// Stop receiving events (except ConfigureNotify, XXX why?) from a window
void win_ev_stop(session_t *ps, win *w);
@ -367,7 +385,7 @@ win_get_bounding_shape_global_by_val(win *w) {
* 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))
static inline margin_t attr_pure
win_calc_frame_extents(const win *w) {
margin_t result = w->frame_extents;
result.top = max_i(result.top, w->g.border_width);
@ -380,7 +398,7 @@ win_calc_frame_extents(const win *w) {
/**
* Check whether a window has WM frames.
*/
static inline bool __attribute__((pure))
static inline bool attr_pure
win_has_frame(const win *w) {
return w->g.border_width
|| w->frame_extents.top || w->frame_extents.left

149
src/x.c
View file

@ -8,8 +8,10 @@
#include <xcb/xfixes.h>
#include <pixman.h>
#include "compiler.h"
#include "common.h"
#include "x.h"
#include "log.h"
/**
* Get a specific attribute of a window.
@ -27,28 +29,27 @@
* 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;
wid_get_prop_adv(const session_t *ps, xcb_window_t w, xcb_atom_t atom, long offset,
long length, xcb_atom_t rtype, int rformat) {
xcb_get_property_reply_t *r = xcb_get_property_reply(ps->c,
xcb_get_property(ps->c, 0, w, atom, rtype, offset, length), 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) {
.ptr = data,
.nitems = nitems,
.type = type,
.format = format,
};
if (r && xcb_get_property_value_length(r) &&
(rtype == XCB_ATOM_ANY || r->type == rtype) &&
(!rformat || r->format == rformat) &&
(r->format == 8 || r->format == 16 || r->format == 32))
{
int len = xcb_get_property_value_length(r);
return (winprop_t) {
.ptr = xcb_get_property_value(r),
.nitems = len/(r->format/8),
.type = r->type,
.format = r->format,
.r = r,
};
}
cxfree(data);
free(r);
return (winprop_t) {
.ptr = NULL,
.nitems = 0,
@ -111,7 +112,7 @@ static inline void x_get_server_pictfmts(session_t *ps) {
xcb_render_query_pict_formats_reply(ps->c,
xcb_render_query_pict_formats(ps->c), &e);
if (e || !ps->pictfmts) {
printf_errf("(): failed to get pict formats\n");
log_fatal("failed to get pict formats\n");
abort();
}
}
@ -141,8 +142,8 @@ x_create_picture_with_pictfmt_and_pixmap(
if (attr) {
xcb_render_create_picture_value_list_serialize(&buf, valuemask, attr);
if (!buf) {
printf_errf("(): failed to serialize picture attributes");
return None;
log_error("failed to serialize picture attributes");
return XCB_NONE;
}
}
@ -152,8 +153,8 @@ x_create_picture_with_pictfmt_and_pixmap(
pixmap, pictfmt->id, valuemask, buf));
free(buf);
if (e) {
printf_errf("(): failed to create picture");
return None;
log_error("failed to create picture");
return XCB_NONE;
}
return tmp_picture;
}
@ -187,7 +188,7 @@ x_create_picture_with_standard_and_pixmap(
* Create an picture.
*/
xcb_render_picture_t
x_create_picture(session_t *ps, int wid, int hei,
x_create_picture_with_pictfmt(session_t *ps, int wid, int hei,
xcb_render_pictforminfo_t *pictfmt, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
{
@ -195,7 +196,7 @@ x_create_picture(session_t *ps, int wid, int hei,
pictfmt = x_get_pictform_for_visual(ps, ps->vis);
if (!pictfmt) {
printf_errf("(): default visual is invalid");
log_fatal("Default visual is invalid");
abort();
}
@ -213,17 +214,26 @@ x_create_picture(session_t *ps, int wid, int hei,
return picture;
}
xcb_render_picture_t
x_create_picture_with_visual(session_t *ps, int w, int h,
xcb_visualid_t visual, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
{
xcb_render_pictforminfo_t *pictfmt = x_get_pictform_for_visual(ps, visual);
return x_create_picture_with_pictfmt(ps, w, h, pictfmt, valuemask, attr);
}
bool x_fetch_region(session_t *ps, xcb_xfixes_region_t r, pixman_region32_t *res) {
xcb_generic_error_t *e = NULL;
xcb_xfixes_fetch_region_reply_t *xr = xcb_xfixes_fetch_region_reply(ps->c,
xcb_xfixes_fetch_region(ps->c, r), &e);
if (!xr) {
printf_errf("(): failed to fetch rectangles");
log_error("Failed to fetch rectangles");
return false;
}
int nrect = xcb_xfixes_fetch_region_rectangles_length(xr);
pixman_box32_t *b = calloc(nrect, sizeof *b);
auto b = ccalloc(nrect, pixman_box32_t);
xcb_rectangle_t *xrect = xcb_xfixes_fetch_region_rectangles(xr);
for (int i = 0; i < nrect; i++) {
b[i] = (pixman_box32_t) {
@ -243,7 +253,7 @@ void x_set_picture_clip_region(session_t *ps, xcb_render_picture_t pict,
int clip_x_origin, int clip_y_origin, const region_t *reg) {
int nrects;
const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
xcb_rectangle_t *xrects = calloc(nrects, sizeof *xrects);
auto xrects = ccalloc(nrects, xcb_rectangle_t);
for (int i = 0; i < nrects; i++)
xrects[i] = (xcb_rectangle_t){
.x = rects[i].x1,
@ -256,12 +266,25 @@ void x_set_picture_clip_region(session_t *ps, xcb_render_picture_t pict,
xcb_request_check(ps->c, xcb_render_set_picture_clip_rectangles_checked(ps->c, pict,
clip_x_origin, clip_y_origin, nrects, xrects));
if (e)
printf_errf("(): failed to set clip region");
log_error("Failed to set clip region");
free(e);
free(xrects);
return;
}
void x_clear_picture_clip_region(session_t *ps, xcb_render_picture_t pict) {
xcb_render_change_picture_value_list_t v = {
.clipmask = None
};
xcb_generic_error_t *e =
xcb_request_check(ps->c, xcb_render_change_picture(ps->c, pict,
XCB_RENDER_CP_CLIP_MASK, &v));
if (e)
log_error("failed to clear clip region");
free(e);
return;
}
/**
* X11 error handler function.
*
@ -276,8 +299,8 @@ x_print_error(unsigned long serial, uint8_t major, uint8_t minor, uint8_t error_
if (major == ps->composite_opcode
&& minor == XCB_COMPOSITE_REDIRECT_SUBWINDOWS) {
fprintf(stderr, "Another composite manager is already running "
"(and does not handle _NET_WM_CM_Sn correctly)\n");
log_fatal("Another composite manager is already running "
"(and does not handle _NET_WM_CM_Sn correctly)");
exit(1);
}
@ -348,16 +371,12 @@ x_print_error(unsigned long serial, uint8_t major, uint8_t minor, uint8_t error_
#undef CASESTRRET2
print_timestamp(ps);
{
char buf[BUF_LEN] = "";
XGetErrorText(ps->dpy, error_code, buf, BUF_LEN);
printf("error %4d %-12s request %4d minor %4d serial %6lu: \"%s\"\n",
error_code, name, major,
minor, serial, buf);
log_debug("X error %d %s request %d minor %d serial %lu: \"%s\"",
error_code, name, major, minor, serial, buf);
}
// print_backtrace();
}
/**
@ -371,8 +390,60 @@ x_create_pixmap(session_t *ps, uint8_t depth, xcb_drawable_t drawable, uint16_t
if (err == NULL)
return pix;
printf_err("Failed to create pixmap:");
log_error("Failed to create pixmap:");
ev_xcb_error(ps, err);
free(err);
return XCB_NONE;
}
/**
* Validate a pixmap.
*
* Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
* are better ways.
*/
bool
x_validate_pixmap(session_t *ps, xcb_pixmap_t 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;
}
/// Names of root window properties that could point to a pixmap of
/// background.
static const char *background_props_str[] = {
"_XROOTPMAP_ID",
"_XSETROOT_ID",
0,
};
xcb_pixmap_t x_get_root_back_pixmap(session_t *ps) {
xcb_pixmap_t pixmap = XCB_NONE;
// Get the values of background attributes
for (int p = 0; background_props_str[p]; p++) {
xcb_atom_t prop_atom = get_atom(ps, background_props_str[p]);
winprop_t prop =
wid_get_prop(ps, ps->root, prop_atom, 1, XCB_ATOM_PIXMAP, 32);
if (prop.nitems) {
pixmap = *prop.p32;
free_winprop(&prop);
break;
}
free_winprop(&prop);
}
return pixmap;
}
bool x_atom_is_background_prop(session_t *ps, xcb_atom_t atom) {
for (int p = 0; background_props_str[p]; p++) {
xcb_atom_t prop_atom = get_atom(ps, background_props_str[p]);
if (prop_atom == atom)
return true;
}
return false;
}

69
src/x.h
View file

@ -10,7 +10,22 @@
#include "region.h"
typedef struct session session_t;
typedef struct winprop winprop_t;
/// Structure representing Window property value.
typedef struct winprop {
union {
void *ptr;
int8_t *p8;
int16_t *p16;
int32_t *p32;
uint32_t *c32; // 32bit cardinal
};
unsigned long nitems;
xcb_atom_t type;
int format;
xcb_get_property_reply_t *r;
} winprop_t;
#define XCB_SYNCED_VOID(func, c, ...) xcb_request_check(c, func##_checked(c, __VA_ARGS__));
#define XCB_SYNCED(func, c, ...) ({ \
@ -51,8 +66,17 @@ x_sync(xcb_connection_t *c) {
* 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);
wid_get_prop_adv(const session_t *ps, xcb_window_t w, xcb_atom_t atom, long offset,
long length, xcb_atom_t rtype, int rformat);
/**
* Wrapper of wid_get_prop_adv().
*/
static inline winprop_t
wid_get_prop(const session_t *ps, xcb_window_t wid, xcb_atom_t atom, long length,
xcb_atom_t rtype, int rformat) {
return wid_get_prop_adv(ps, wid, atom, 0L, length, rtype, rformat);
}
/**
* Get the value of a type-<code>Window</code> property of a window.
@ -74,34 +98,41 @@ xcb_render_picture_t x_create_picture_with_pictfmt_and_pixmap(
session_t *ps, xcb_render_pictforminfo_t *pictfmt,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
__attribute__((nonnull(1, 2)));
attr_nonnull(1, 2);
xcb_render_picture_t x_create_picture_with_visual_and_pixmap(
session_t *ps, xcb_visualid_t visual,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
__attribute__((nonnull(1)));
attr_nonnull(1);
xcb_render_picture_t x_create_picture_with_standard_and_pixmap(
session_t *ps, xcb_pict_standard_t standard,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
__attribute__((nonnull(1)));
attr_nonnull(1);
/**
* Create an picture.
*/
xcb_render_picture_t
x_create_picture(session_t *ps, int wid, int hei,
x_create_picture_with_pictfmt(session_t *ps, int wid, int hei,
xcb_render_pictforminfo_t *pictfmt, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr);
xcb_render_picture_t
x_create_picture_with_visual(session_t *ps, int w, int h,
xcb_visualid_t visual, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr);
/// Fetch a X region and store it in a pixman region
bool x_fetch_region(session_t *ps, xcb_xfixes_region_t r, region_t *res);
void x_set_picture_clip_region(session_t *ps, xcb_render_picture_t,
int clip_x_origin, int clip_y_origin, const region_t *);
void x_clear_picture_clip_region(session_t *ps, xcb_render_picture_t pict);
/**
* X11 error handler function.
*
@ -112,3 +143,27 @@ x_print_error(unsigned long serial, uint8_t major, uint8_t minor, uint8_t error_
xcb_pixmap_t
x_create_pixmap(session_t *ps, uint8_t depth, xcb_drawable_t drawable, uint16_t width, uint16_t height);
bool
x_validate_pixmap(session_t *ps, xcb_pixmap_t pxmap);
/**
* Free a <code>winprop_t</code>.
*
* @param pprop pointer to the <code>winprop_t</code> to free.
*/
static inline void
free_winprop(winprop_t *pprop) {
// Empty the whole structure to avoid possible issues
if (pprop->r)
free(pprop->r);
pprop->ptr = NULL;
pprop->r = NULL;
pprop->nitems = 0;
}
/// Get the back pixmap of the root window
xcb_pixmap_t x_get_root_back_pixmap(session_t *ps);
/// Return true if the atom refers to a property name that is used for the
/// root window background pixmap
bool x_atom_is_background_prop(session_t *ps, xcb_atom_t atom);

View file

@ -1,6 +1,9 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2014 Richard Grenville <pyxlcy@gmail.com>
#include "compiler.h"
#include "log.h"
#include "xrescheck.h"
static xrc_xid_record_t *gs_xid_records = NULL;
@ -21,7 +24,7 @@ static xrc_xid_record_t *gs_xid_records = NULL;
*/
void
xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS) {
xrc_xid_record_t *prec = cmalloc(1, xrc_xid_record_t);
auto prec = ccalloc(1, xrc_xid_record_t);
prec->xid = xid;
prec->type = type;
M_CPY_POS_DATA(prec);
@ -37,8 +40,8 @@ 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);
log_error("XRC: %s:%d %s(): Can't find XID %#010lx we want to delete.",
file, line, func, xid);
return;
}
HASH_DEL(gs_xid_records, prec);
@ -51,8 +54,8 @@ xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS) {
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);
log_trace("XRC: %s:%d %s(): %#010lx (%s) not freed.\n",
prec->file, prec->line, prec->func, prec->xid, prec->type);
}
/**

View file

@ -1,36 +0,0 @@
#!/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 "${@}"