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:
commit
252bbaa94c
59 changed files with 7877 additions and 4903 deletions
98
.circleci/config.yml
Normal file
98
.circleci/config.yml
Normal 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:
|
|
@ -2,3 +2,4 @@ root = true
|
|||
[*.{c,h}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
max_line_length = 90
|
||||
|
|
8
.github/issue_template.md
vendored
8
.github/issue_template.md
vendored
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
197
Makefile
|
@ -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
|
14
README.md
14
README.md
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
--------
|
||||
|
|
|
@ -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
|
||||
--------
|
||||
|
|
|
@ -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
|
||||
|
|
16
meson.build
16
meson.build
|
@ -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')
|
||||
|
|
|
@ -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
11
src/backend/backend.c
Normal 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
130
src/backend/backend.h
Normal 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 *);
|
116
src/backend/backend_common.c
Normal file
116
src/backend/backend_common.c
Normal 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 :
|
10
src/backend/backend_common.h
Normal file
10
src/backend/backend_common.h
Normal 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
575
src/backend/gl/gl_common.c
Normal 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
160
src/backend/gl/gl_common.h
Normal 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(®_new, dx, dy, width, height); \
|
||||
pixman_region32_intersect(®_new, ®_new, (region_t *)reg_tgt); \
|
||||
rects = pixman_region32_rectangles(®_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(®_new); \
|
||||
} \
|
||||
while (0)
|
882
src/backend/gl/glx.c
Normal file
882
src/backend/gl/glx.c
Normal 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
50
src/backend/gl/glx.h
Normal 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
12
src/backend/meson.build
Normal 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
464
src/backend/xrender.c
Normal 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(®_tmp);
|
||||
// Shadow doesn't need to be painted underneath the body of the window
|
||||
// Because no one can see it
|
||||
pixman_region32_subtract(®_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(®_tmp, ®_tmp,
|
||||
&ps->shadow_exclude_reg);
|
||||
|
||||
// Might be worth while to crop the region to shadow border
|
||||
pixman_region32_intersect_rect(®_tmp, ®_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(®_tmp, ®_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(
|
||||
®_tmp, ®_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(®_tmp, ®_tmp, &bshape);
|
||||
pixman_region32_fini(&bshape);
|
||||
|
||||
// Detect if the region is empty before painting
|
||||
if (pixman_region32_not_empty(®_tmp)) {
|
||||
x_set_picture_clip_region(ps, xd->target_buffer, 0, 0, ®_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(®_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(®_paint_local);
|
||||
pixman_region32_copy(®_paint_local, (region_t *)reg_paint);
|
||||
pixman_region32_translate(®_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, ®_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
200
src/c2.c
|
@ -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
|
||||
}
|
||||
|
|
14
src/c2.h
14
src/c2.h
|
@ -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);
|
||||
|
|
749
src/common.h
749
src/common.h
|
@ -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
93
src/compiler.h
Normal 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
|
3173
src/compton.c
3173
src/compton.c
File diff suppressed because it is too large
Load diff
119
src/compton.h
119
src/compton.h
|
@ -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 :
|
||||
|
|
159
src/config.c
159
src/config.c
|
@ -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;
|
||||
}
|
||||
|
|
366
src/config.h
366
src/config.h
|
@ -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 :
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
338
src/dbus.c
338
src/dbus.c
|
@ -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);
|
||||
}
|
||||
//!@}
|
||||
|
|
205
src/dbus.h
205
src/dbus.h
|
@ -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 :
|
||||
|
|
|
@ -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
116
src/kernel.c
Normal 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
19
src/kernel.h
Normal 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
341
src/log.c
Normal 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
79
src/log.h
Normal 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:
|
|
@ -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)
|
||||
|
|
221
src/opengl.c
221
src/opengl.c
|
@ -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);
|
||||
|
|
83
src/opengl.h
83
src/opengl.h
|
@ -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
862
src/options.c
Normal 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
34
src/options.h
Normal 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:
|
|
@ -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
1333
src/render.c
Normal file
File diff suppressed because it is too large
Load diff
39
src/render.h
Normal file
39
src/render.h
Normal 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
43
src/string_utils.c
Normal 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
54
src/string_utils.h
Normal 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
34
src/utils.c
Normal 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 :
|
67
src/utils.h
67
src/utils.h
|
@ -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
301
src/vsync.c
Normal 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
9
src/vsync.h
Normal 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
120
src/win.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
42
src/win.h
42
src/win.h
|
@ -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
149
src/x.c
|
@ -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
69
src/x.h
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 "${@}"
|
Loading…
Add table
Reference in a new issue