From 58c0ecec40861e89c95f7f39a91f7ebdf55e3838 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 19 Jan 2013 20:20:27 +0800 Subject: [PATCH] Feature #80: D-Bus support - Add D-Bus support. Currently 7 methods are available: "reset" (same as SIGUSR1), "list_win" (list the windows compton manages), "win_get" (get a property of the window), "win_set" (set a property of the window), "find_win" (find window based on client window / focus), "opts_get" (get the value of a compton option), and "opts_set" (set the value of a compton option), together with 4 signals: "win_added", "win_destroyed", "win_mapped", "win_unmapped". - D-Bus support depends on libdbus. - As there are many items and my time is tight, no much tests are done. Bugs to be expected. - Create a new header file `common.h` that contains shared content. - Fix some bugs in timeout handling. - Update file headers in all source files. - Re-enable --unredir-if-possible on multi-screen set-ups, as the user could turn if off manually anyway. - Check if the window is mapped in `repair_win()`. - Add ps->track_atom_lst and its handlers, to prepare for the new condition format. - Known issue 1: "win_get", "win_set", "opts_get", "opts_set" support a very limited number of targets only. New ones will be added gradually. - Known issue 2: Accidental drop of D-Bus connection is not handled. - Known issue 3: Introspection does not reveal all available methods, because some methods have unpredictable prototypes. Still hesitating about what to do... - Known issue 4: Error handling is not finished yet. Compton does not always reply with the correct error message (but it does print out the correct error message, usually). --- Makefile | 14 +- README.md | 22 +- dbus-examples/cdbus-driver.sh | 45 ++ src/common.h | 1370 +++++++++++++++++++++++++++++++++ src/compton.c | 155 +++- src/compton.h | 1256 +----------------------------- src/dbus.c | 920 ++++++++++++++++++++++ src/dbus.h | 213 +++++ 8 files changed, 2715 insertions(+), 1280 deletions(-) create mode 100755 dbus-examples/cdbus-driver.sh create mode 100644 src/common.h create mode 100644 src/dbus.c create mode 100644 src/dbus.h diff --git a/Makefile b/Makefile index 663973e..1d4c8e7 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ PACKAGES = x11 xcomposite xfixes xdamage xrender xext xrandr LIBS = -lm -lrt INCS = +OBJS = compton.o + # === Configuration flags === CFG = @@ -46,10 +48,11 @@ ifeq "$(NO_VSYNC_OPENGL)" "" endif # ==== D-Bus ==== -# ifeq "$(NO_DBUS)" "" -# CFG += -DCONFIG_DBUS -# PACKAGES += dbus-1 -# endif +ifeq "$(NO_DBUS)" "" + CFG += -DCONFIG_DBUS + PACKAGES += dbus-1 + OBJS += dbus.o +endif # === Version string === COMPTON_VERSION ?= git-$(shell git describe --always --dirty)-$(shell git log -1 --date=short --pretty=format:%cd) @@ -63,14 +66,13 @@ LIBS += $(shell pkg-config --libs $(PACKAGES)) INCS += $(shell pkg-config --cflags $(PACKAGES)) CFLAGS += -Wall -std=c99 -OBJS = compton.o MANPAGES = man/compton.1 man/compton-trans.1 MANPAGES_HTML = $(addsuffix .html,$(MANPAGES)) # === Recipes === .DEFAULT_GOAL := compton -%.o: src/%.c src/%.h +%.o: src/%.c src/%.h src/common.h $(CC) $(CFLAGS) $(INCS) -c src/$*.c compton: $(OBJS) diff --git a/README.md b/README.md index bbdee8f..ab3a797 100644 --- a/README.md +++ b/README.md @@ -11,17 +11,18 @@ partially doing this out of a desire to learn Xlib. ## Changes from xcompmgr: -* __inactive window transparency__ (specified with `-i`) +* __inactive window transparency / dimming__ * __titlebar/frame transparency__ (specified with `-e`) * menu transparency (thanks to Dana) * shadows are now enabled for argb windows, e.g. terminals with transparency * removed serverside shadows (and simple compositing) to clean the code, the only option that remains is clientside shadows -* configuration files (specified with `--config`) -* colored shadows (with `--shadow-[red/green/blue] value`) +* configuration files (see the man page for more details) +* colored shadows (`--shadow-[red/green/blue]`) * a new fade system -* vsync (still under development) -* several more options +* VSync support (not always working) +* Blur of background of transparent windows, window color inversion (bad in performance) +* Some more options... ## Fixes from the original xcompmgr: @@ -51,10 +52,11 @@ __R__ for runtime * xproto / x11proto (B) * bash (R) * xprop,xwininfo / x11-utils (R) -* libpcre (B,R) (Will probably be made optional soon) -* libconfig (B,R) (Will probably be made optional soon) -* libdrm (B) (Will probably be made optional soon) -* libGL (B,R) (Will probably be made optional soon) +* libpcre (B,R) (Can be disabled with `NO_REGEX_PCRE` at compile time) +* libconfig (B,R) (Can be disabled with `NO_LIBCONFIG` at compile time) +* libdrm (B) (Can be disabled with `NO_VSYNC_DRM` at compile time) +* libGL (B,R) (Can be disabled with `NO_VSYNC_OPENGL` at compile time) +* libdbus (B,R) (Can be disabled with `NO_DBUS` at compile time) * asciidoc (B) ### How to build @@ -64,7 +66,7 @@ To build, make sure you have the dependencies above: ``` bash # Make the main program $ make -# Make the newer man pages +# Make the man pages $ make docs # Install $ make install diff --git a/dbus-examples/cdbus-driver.sh b/dbus-examples/cdbus-driver.sh new file mode 100755 index 0000000..61b6135 --- /dev/null +++ b/dbus-examples/cdbus-driver.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# === Get connection parameters === + +dpy=$(echo -n "$DISPLAY" | tr -c '[:alnum:]' _) + +if [ -z "$dpy" ]; then + echo "Cannot find display." + exit 1 +fi + +service="com.github.chjj.compton.${dpy}" +interface='com.github.chjj.compton' +object='/com/github/chjj/compton' +type_win='uint32' +type_enum='uint16' + +# === DBus methods === + +# List all window ID compton manages (except destroyed ones) +dbus-send --print-reply --dest="$service" "$object" "${interface}.list_win" + +# Get window ID of currently focused window +focused=$(dbus-send --print-reply --dest="$service" "$object" "${interface}.find_win" string:focused | sed -n 's/^[[:space:]]*'${type_win}'\s*\([[:digit:]]*\).*/\1/p') + +if [ -n "$focused" ]; then + # Get invert_color_force property of the window + dbus-send --print-reply --dest="$service" "$object" "${interface}.win_get" "${type_win}:${focused}" string:invert_color_force + + # Set the window to have inverted color + dbus-send --print-reply --dest="$service" "$object" "${interface}.win_set" "${type_win}:${focused}" string:invert_color_force "${type_enum}:1" +else + echo "Cannot find focused window." +fi + +# Set the clear_shadow setting to true +dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_set" string:clear_shadow boolean:true + +# Get the clear_shadow setting +dbus-send --print-reply --dest="$service" "$object" "${interface}.opts_get" string:clear_shadow + +# Reset compton +sleep 3 +dbus-send --print-reply --dest="$service" "$object" "${interface}.reset" + diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..31c9c20 --- /dev/null +++ b/src/common.h @@ -0,0 +1,1370 @@ +/* + * Compton - a compositor for X11 + * + * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard + * + * Copyright (c) 2011-2013, Christopher Jeffrey + * See LICENSE for more information. + * + */ + +// === Options === + +// Debug options, enable them using -D in CFLAGS +// #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 MONITOR_REPAINT 1 + +// Whether to enable PCRE regular expression support in blacklists, enabled +// by default +// #define CONFIG_REGEX_PCRE 1 +// Whether to enable JIT support of libpcre. This may cause problems on PaX +// kernels. +// #define CONFIG_REGEX_PCRE_JIT 1 +// Whether to enable parsing of configuration files using libconfig. +// #define CONFIG_LIBCONFIG 1 +// Whether we are using a legacy version of libconfig (1.3.x). +// #define CONFIG_LIBCONFIG_LEGACY 1 +// Whether to enable DRM VSync support +// #define CONFIG_VSYNC_DRM 1 +// Whether to enable OpenGL VSync support +// #define CONFIG_VSYNC_OPENGL 1 +// Whether to enable DBus support with libdbus. +// #define CONFIG_DBUS 1 + +// === Includes === + +// For some special functions +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// libpcre +#ifdef CONFIG_REGEX_PCRE +#include + +// For compatiblity with +#include +#endif + +// libdbus +#ifdef CONFIG_DBUS +#include +#endif + +// libGL +#ifdef CONFIG_VSYNC_OPENGL +#include +#endif + +// === Macros === + +#define MSTR_(s) #s +#define MSTR(s) MSTR_(s) + +/// 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 + +// === Constants === +#if !(COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2) +#error libXcomposite version unsupported +#endif + +#define ROUNDED_PERCENT 0.05 +#define ROUNDED_PIXELS 10 + +#define OPAQUE 0xffffffff +#define REGISTER_PROP "_NET_WM_CM_S" + +#define TIME_MS_MAX LONG_MAX +#define FADE_DELTA_TOLERANCE 0.2 +#define SWOPTI_TOLERANCE 3000 +#define TIMEOUT_RUN_TOLERANCE 0.05 +#define WIN_GET_LEADER_MAX_RECURSION 20 + +#define SEC_WRAP (15L * 24L * 60L * 60L) + +#define NS_PER_SEC 1000000000L +#define US_PER_SEC 1000000L +#define MS_PER_SEC 1000 + +#define XRFILTER_CONVOLUTION "convolution" +#define XRFILTER_GUASSIAN "gaussian" +#define XRFILTER_BINOMIAL "binomial" + +// Window flags + +// Window size is changed +#define WFLAG_SIZE_CHANGE 0x0001 +// Window size/position is changed +#define WFLAG_POS_CHANGE 0x0002 +// Window opacity / dim state changed +#define WFLAG_OPCT_CHANGE 0x0004 + +// === Types === + +typedef uint32_t opacity_t; +typedef long time_ms_t; + +typedef enum { + WINTYPE_UNKNOWN, + WINTYPE_DESKTOP, + WINTYPE_DOCK, + WINTYPE_TOOLBAR, + WINTYPE_MENU, + WINTYPE_UTILITY, + WINTYPE_SPLASH, + WINTYPE_DIALOG, + WINTYPE_NORMAL, + WINTYPE_DROPDOWN_MENU, + WINTYPE_POPUP_MENU, + WINTYPE_TOOLTIP, + WINTYPE_NOTIFY, + WINTYPE_COMBO, + WINTYPE_DND, + NUM_WINTYPES +} wintype_t; + +/// Enumeration type to represent switches. +typedef enum { + OFF, // false + ON, // true + UNSET +} switch_t; + +/// Enumeration type of window painting mode. +typedef enum { + WMODE_TRANS, + WMODE_SOLID, + WMODE_ARGB +} winmode_t; + +/// Structure representing needed window updates. +typedef struct { + bool shadow : 1; + bool fade : 1; + bool focus : 1; + bool invert_color : 1; +} win_upd_t; + +/// Structure representing Window property value. +typedef struct { + // All pointers have the same length, right? + // I wanted to use anonymous union but it's a GNU extension... + union { + unsigned char *p8; + short *p16; + long *p32; + } data; + unsigned long nitems; + Atom type; + int format; +} winprop_t; + +typedef struct _ignore { + struct _ignore *next; + unsigned long sequence; +} ignore_t; + +enum wincond_target { + CONDTGT_NAME, + CONDTGT_CLASSI, + CONDTGT_CLASSG, + CONDTGT_ROLE, +}; + +enum wincond_type { + CONDTP_EXACT, + CONDTP_ANYWHERE, + CONDTP_FROMSTART, + CONDTP_WILDCARD, + CONDTP_REGEX_PCRE, +}; + +#define CONDF_IGNORECASE 0x0001 + +typedef struct _wincond { + enum wincond_target target; + enum wincond_type type; + char *pattern; +#ifdef CONFIG_REGEX_PCRE + pcre *regex_pcre; + pcre_extra *regex_pcre_extra; +#endif + int16_t flags; + struct _wincond *next; +} wincond_t; + +/// VSync modes. +typedef enum { + VSYNC_NONE, + VSYNC_DRM, + VSYNC_OPENGL, +} vsync_t; + +#ifdef CONFIG_VSYNC_OPENGL +typedef int (*f_WaitVideoSync) (int, int, unsigned *); +typedef int (*f_GetVideoSync) (unsigned *); +#endif + +typedef struct { + int size; + double *data; +} conv; + +/// Linked list type of atoms. +typedef struct _latom { + Atom atom; + struct _latom *next; +} latom_t; + +struct _timeout_t; + +struct _win; + +/// Structure representing all options. +typedef struct { + // === General === + char *display; + /// 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 fork to background. + bool fork_after_register; + /// Whether to detect rounded corners. + bool detect_rounded_corners; + /// Whether to paint on X Composite overlay window instead of root + /// window. + bool paint_on_overlay; + /// Whether to unredirect all windows if a full-screen opaque window + /// is detected. + bool unredir_if_possible; + /// Whether to enable D-Bus support. + bool dbus; + /// Path to log file. + char *logpath; + /// Whether to work under synchronized mode for debugging. + bool synchronize; + + // === VSync & software optimization === + /// User-specified refresh rate. + int refresh_rate; + /// Whether to enable refresh-rate-based software optimization. + bool sw_opti; + /// VSync method to use; + vsync_t vsync; + /// Whether to enable double buffer. + bool dbe; + /// Whether to do VSync aggressively. + bool vsync_aggressive; + + // === Shadow === + /// Enable/disable shadow for specific window types. + bool wintype_shadow[NUM_WINTYPES]; + /// Red, green and blue tone of the shadow. + double shadow_red, shadow_green, shadow_blue; + int shadow_radius; + int shadow_offset_x, shadow_offset_y; + double shadow_opacity; + bool clear_shadow; + /// Shadow blacklist. A linked list of conditions. + wincond_t *shadow_blacklist; + /// Whether bounding-shaped window should be ignored. + bool shadow_ignore_shaped; + /// Whether to respect _COMPTON_SHADOW. + bool respect_prop_shadow; + + // === 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; + /// Fading blacklist. A linked list of conditions. + wincond_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; + /// 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; + /// 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. + wincond_t *invert_color_list; + + // === Focus related === + /// Consider windows of specific types to be always focused. + bool wintype_focus[NUM_WINTYPES]; + /// 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. + wincond_t *focus_blacklist; + /// Whether to do window grouping with WM_TRANSIENT_FOR. + bool detect_transient; + /// Whether to do window grouping with WM_CLIENT_LEADER. + bool detect_client_leader; + + // === Calculated === + /// Whether compton needs to track focus changes. + bool track_focus; + /// Whether compton needs to track window name and class. + bool track_wdata; + /// Whether compton needs to track window leaders. + bool track_leader; +} options_t; + +/// Structure containing all necessary data for a compton session. +typedef struct { + // === Display related === + /// Display in use. + Display *dpy; + /// Default screen. + int scr; + /// Default visual. + Visual *vis; + /// Default depth. + int depth; + /// Root window. + Window root; + /// Height of root window. + int root_height; + /// Width of root window. + int root_width; + // Damage of root window. + // Damage root_damage; + /// X Composite overlay window. Used if --paint-on-overlay. + Window overlay; + /// Picture of the root window background. + Picture root_tile; + /// A region of the size of the screen. + XserverRegion screen_reg; + /// Picture of root window. Destination of painting in no-DBE painting + /// mode. + Picture root_picture; + /// A Picture acting as the painting target. + Picture tgt_picture; + /// Temporary buffer to paint to before sending to display. + Picture tgt_buffer; + /// DBE back buffer for root window. Used in DBE painting mode. + XdbeBackBuffer root_dbe; + /// Window ID of the window we register as a symbol. + Window reg_win; + + // === Operation related === + /// Program options. + options_t o; + /// File descriptors to check for reading. + fd_set *pfds_read; + /// File descriptors to check for writing. + fd_set *pfds_write; + /// File descriptors to check for exceptions. + fd_set *pfds_except; + /// Largest file descriptor in fd_set-s above. + int nfds_max; + /// Linked list of all timeouts. + struct _timeout_t *tmout_lst; + /// Whether we have received an event in this cycle. + bool ev_received; + /// Whether the program is idling. I.e. no fading, no potential window + /// changes. + bool idling; + /// Program start time. + struct timeval time_start; + /// The region needs to painted on next paint. + XserverRegion all_damage; + /// Whether all windows are currently redirected. + bool redirected; + /// Whether there's a highest full-screen window, and all windows could + /// be unredirected. + bool unredir_possible; + /// Pre-generated alpha pictures. + Picture *alpha_picts; + /// Whether all reg_ignore of windows should expire in this paint. + bool reg_ignore_expire; + /// Time of last fading. In milliseconds. + time_ms_t fade_time; + /// Head pointer of the error ignore linked list. + ignore_t *ignore_head; + /// Pointer to the next member of tail element of the error + /// ignore linked list. + ignore_t **ignore_tail; + /// Reset program after next paint. + bool reset; + + // === Expose event related === + /// Pointer to an array of XRectangle-s of exposed region. + XRectangle *expose_rects; + /// Number of XRectangle-s in expose_rects. + int size_expose; + /// Index of the next free slot in expose_rects. + int n_expose; + + // === Window related === + /// Linked list of all windows. + struct _win *list; + /// Pointer to win of current active window. Used by + /// EWMH _NET_ACTIVE_WINDOW focus detection. In theory, + /// it's more reliable to store the window ID directly here, just in + /// case the WM does something extraordinary, but caching the pointer + /// means another layer of complexity. + struct _win *active_win; + /// Window ID of leader window of currently active window. Used for + /// subsidiary window detection. + Window active_leader; + + // === Shadow/dimming related === + /// 1x1 black Picture. + Picture black_picture; + /// 1x1 Picture of the shadow color. + Picture cshadow_picture; + /// 1x1 white Picture. + Picture white_picture; + /// Gaussian map of shadow. + conv *gaussian_map; + // for shadow precomputation + /// Shadow depth on one side. + int cgsize; + /// Pre-computed color table for corners of shadow. + unsigned char *shadow_corner; + /// Pre-computed color table for a side of shadow. + unsigned char *shadow_top; + + // === Software-optimization-related === + /// Currently used refresh rate. + short refresh_rate; + /// Interval between refresh in nanoseconds. + long refresh_intv; + /// Nanosecond offset of the first painting. + long paint_tm_offset; + + #ifdef CONFIG_VSYNC_DRM + // === DRM VSync related === + /// File descriptor of DRI device file. Used for DRM VSync. + int drm_fd; + #endif + + #ifdef CONFIG_VSYNC_OPENGL + // === OpenGL VSync related === + /// GLX context. + GLXContext glx_context; + /// Pointer to glXGetVideoSyncSGI function. + f_GetVideoSync glx_get_video_sync; + /// Pointer to glXWaitVideoSyncSGI function. + f_WaitVideoSync glx_wait_video_sync; + #endif + + // === X extension related === + /// Event base number for X Fixes extension. + int xfixes_event; + /// Error base number for X Fixes extension. + int xfixes_error; + /// Event base number for X Damage extension. + int damage_event; + /// Error base number for X Damage extension. + int damage_error; + /// Event base number for X Render extension. + int render_event; + /// Error base number for X Render extension. + int render_error; + /// Event base number for X Composite extension. + int composite_event; + /// Error base number for X Composite extension. + int composite_error; + /// Major opcode for X Composite extension. + int composite_opcode; + /// Whether X Composite NameWindowPixmap is available. Aka if X + /// Composite version >= 0.2. + bool has_name_pixmap; + /// Whether X Shape extension exists. + bool shape_exists; + /// Event base number for X Shape extension. + int shape_event; + /// Error base number for X Shape extension. + int shape_error; + /// Whether X RandR extension exists. + bool randr_exists; + /// Event base number for X RandR extension. + int randr_event; + /// Error base number for X RandR extension. + int randr_error; + #ifdef CONFIG_VSYNC_OPENGL + /// Whether X GLX extension exists. + bool glx_exists; + /// Event base number for X GLX extension. + int glx_event; + /// Error base number for X GLX extension. + int glx_error; + #endif + /// Whether X DBE extension exists. + bool dbe_exists; + /// Whether X Render convolution filter exists. + bool xrfilter_convolution_exists; + + // === Atoms === + /// Atom of property _NET_WM_OPACITY. + Atom atom_opacity; + /// Atom of _NET_FRAME_EXTENTS. + Atom atom_frame_extents; + /// Property atom to identify top-level frame window. Currently + /// WM_STATE. + Atom atom_client; + /// Atom of property WM_NAME. + Atom atom_name; + /// Atom of property _NET_WM_NAME. + Atom atom_name_ewmh; + /// Atom of property WM_CLASS. + Atom atom_class; + /// Atom of property WM_WINDOW_ROLE. + Atom atom_role; + /// Atom of property WM_TRANSIENT_FOR. + Atom atom_transient; + /// Atom of property WM_CLIENT_LEADER. + Atom atom_client_leader; + /// Atom of property _NET_ACTIVE_WINDOW. + Atom atom_ewmh_active_win; + /// Atom of property _COMPTON_SHADOW. + Atom atom_compton_shadow; + /// Atom of property _NET_WM_WINDOW_TYPE. + Atom atom_win_type; + /// Array of atoms of all possible window types. + Atom atoms_wintypes[NUM_WINTYPES]; + /// Linked list of additional atoms to track. + latom_t *track_atom_lst; + +#ifdef CONFIG_DBUS + // === DBus related === + // DBus connection. + DBusConnection *dbus_conn; + // DBus service name. + char *dbus_service; +#endif +} session_t; + +/// Structure representing a top-level window compton manages. +typedef struct _win { + /// Pointer to the next structure in the linked list. + struct _win *next; + /// Pointer to the next higher window to paint. + struct _win *prev_trans; + + // Core members + /// ID of the top-level frame window. + Window id; + /// Window attributes. + XWindowAttributes a; + /// Window painting mode. + winmode_t mode; + /// Whether the window has been damaged at least once. + bool damaged; + /// Damage of the window. + Damage damage; + /// NameWindowPixmap of the window. + Pixmap pixmap; + /// Picture of the window. + Picture picture; + /// Bounding shape of the window. + XserverRegion border_size; + /// Region of the whole window, shadow region included. + XserverRegion extents; + /// Window flags. Definitions above. + int_fast16_t flags; + /// Whether there's a pending ConfigureNotify happening + /// when the window is unmapped. + bool need_configure; + /// Queued ConfigureNotify when the window is unmapped. + XConfigureEvent queue_configure; + /// Region to be ignored when painting. Basically the region where + /// higher opaque windows will paint upon. Depends on window frame + /// opacity state, window geometry, window mapped/unmapped state, + /// window mode, of this and all higher windows. + XserverRegion reg_ignore; + /// Cached width/height of the window including border. + int widthb, heightb; + /// Whether the window has been destroyed. + bool destroyed; + /// Whether the window is bounding-shaped. + bool bounding_shaped; + /// Whether the window just have rounded corners. + bool rounded_corners; + /// Whether this window is to be painted. + bool to_paint; + /// Whether this window is in open/close state. + bool in_openclose; + + // Client window related members + /// ID of the top-level client window of the window. + Window client_win; + /// Type of the window. + wintype_t window_type; + /// Whether it looks like a WM window. We consider a window WM window if + /// it does not have a decedent with WM_STATE and it is not override- + /// redirected itself. + bool wmwin; + /// Leader window ID of the window. + Window leader; + /// Cached topmost window ID of the window. + Window cache_leader; + + // Focus-related members + /// Whether the window is to be considered focused. + bool focused; + /// Override value of window focus state. Set by D-Bus method calls. + switch_t focused_force; + /// Whether the window is actually focused. + bool focused_real; + + // Blacklist related members + /// Name of the window. + char *name; + /// Window instance class of the window. + char *class_instance; + /// Window general class of the window. + char *class_general; + /// WM_WINDOW_ROLE value of the window. + char *role; + wincond_t *cache_sblst; + wincond_t *cache_fblst; + wincond_t *cache_fcblst; + wincond_t *cache_ivclst; + + // Opacity-related members + /// Current window opacity. + opacity_t opacity; + /// Target window opacity. + opacity_t opacity_tgt; + /// Cached value of opacity window attribute. + opacity_t opacity_prop; + /// Cached value of opacity window attribute on client window. For + /// broken window managers not transferring client window's + /// _NET_WM_OPACITY value + opacity_t opacity_prop_client; + /// Alpha mask Picture to render window with opacity. + Picture alpha_pict; + + // Fading-related members + /// Do not fade if it's false. Change on window type change. + /// Used by fading blacklist in the future. + bool fade; + /// Callback to be called after fading completed. + void (*fade_callback) (session_t *ps, struct _win *w); + + // Frame-opacity-related members + /// Current window frame opacity. Affected by window opacity. + double frame_opacity; + /// Alpha mask Picture to render window frame with opacity. + Picture frame_alpha_pict; + /// Frame widths. Determined by client window attributes. + unsigned int left_width, right_width, top_width, bottom_width; + + // Shadow-related members + /// Whether a window has shadow. Calculated. + bool shadow; + /// Override value of window shadow state. Set by D-Bus method calls. + switch_t shadow_force; + /// Opacity of the shadow. Affected by window opacity and frame opacity. + double shadow_opacity; + /// X offset of shadow. Affected by commandline argument. + int shadow_dx; + /// Y offset of shadow. Affected by commandline argument. + int shadow_dy; + /// Width of shadow. Affected by window size and commandline argument. + int shadow_width; + /// Height of shadow. Affected by window size and commandline argument. + int shadow_height; + /// Picture to render shadow. Affected by window size. + Picture shadow_pict; + /// Alpha mask Picture to render shadow. Affected by shadow opacity. + Picture shadow_alpha_pict; + /// The value of _COMPTON_SHADOW attribute of the window. Below 0 for + /// none. + long prop_shadow; + + // Dim-related members + /// Whether the window is to be dimmed. + bool dim; + /// Picture for dimming. Affected by user-specified inactive dim + /// opacity and window opacity. + Picture dim_alpha_pict; + + /// Whether to invert window color. + bool invert_color; + /// Override value of window color inversion state. Set by D-Bus method + /// calls. + switch_t invert_color_force; +} win; + +/// Temporary structure used for communication between +/// get_cfg() and parse_config(). +struct options_tmp { + bool no_dock_shadow; + bool no_dnd_shadow; + double menu_opacity; +}; + +/// Structure for a recorded timeout. +typedef struct _timeout_t { + bool enabled; + void *data; + bool (*callback)(session_t *ps, struct _timeout_t *ptmout); + time_ms_t interval; + time_ms_t firstrun; + time_ms_t lastrun; + struct _timeout_t *next; +} timeout_t; + +/// Enumeration for window event hints. +typedef enum { + WIN_EVMODE_UNKNOWN, + WIN_EVMODE_FRAME, + WIN_EVMODE_CLIENT +} win_evmode_t; + +extern const char *WINTYPES[NUM_WINTYPES]; +extern session_t *ps_g; + +// == Debugging code == +static inline void +print_timestamp(session_t *ps); + +#ifdef DEBUG_ALLOC_REG + +#include +#define BACKTRACE_SIZE 5 + +/** + * Print current backtrace, excluding the first two items. + * + * 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 = 2; i < size; i++) + printf ("%s\n", strings[i]); + + free(strings); +} + +/** + * Wrapper of XFixesCreateRegion, for debugging. + */ +static inline XserverRegion +XFixesCreateRegion_(Display *dpy, XRectangle *p, int n, + const char *func, int line) { + XserverRegion reg = XFixesCreateRegion(dpy, p, n); + print_timestamp(ps_g); + printf("%#010lx: XFixesCreateRegion() in %s():%d\n", reg, func, line); + print_backtrace(); + fflush(stdout); + return reg; +} + +/** + * Wrapper of XFixesDestroyRegion, for debugging. + */ +static inline void +XFixesDestroyRegion_(Display *dpy, XserverRegion reg, + const char *func, int line) { + XFixesDestroyRegion(dpy, reg); + print_timestamp(ps_g); + printf("%#010lx: XFixesDestroyRegion() in %s():%d\n", reg, func, line); + fflush(stdout); +} + +#define XFixesCreateRegion(dpy, p, n) XFixesCreateRegion_(dpy, p, n, __func__, __LINE__) +#define XFixesDestroyRegion(dpy, reg) XFixesDestroyRegion_(dpy, reg, __func__, __LINE__) +#endif + +// === Functions === + +/** + * Return whether a struct timeval value is empty. + */ +static inline bool +timeval_isempty(struct timeval *ptv) { + if (!ptv) + return false; + + return ptv->tv_sec <= 0 && ptv->tv_usec <= 0; +} + +/** + * Compare a struct timeval with a time in milliseconds. + * + * @return > 0 if ptv > ms, 0 if ptv == 0, -1 if ptv < ms + */ +static inline int +timeval_ms_cmp(struct timeval *ptv, time_ms_t ms) { + assert(ptv); + + // We use those if statement instead of a - expression because of possible + // truncation problem from long to int. + { + long sec = ms / MS_PER_SEC; + if (ptv->tv_sec > sec) + return 1; + if (ptv->tv_sec < sec) + return -1; + } + + { + long usec = ms % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC); + if (ptv->tv_usec > usec) + return 1; + if (ptv->tv_usec < usec) + return -1; + } + + return 0; +} + +/** + * Subtracting two struct timeval values. + * + * Taken from glibc manual. + * + * Subtract the `struct timeval' values X and Y, + * storing the result in RESULT. + * Return 1 if the difference is negative, otherwise 0. + */ +static inline int +timeval_subtract(struct timeval *result, + struct timeval *x, + struct timeval *y) { + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_usec < y->tv_usec) { + long nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; + y->tv_usec -= 1000000 * nsec; + y->tv_sec += nsec; + } + + if (x->tv_usec - y->tv_usec > 1000000) { + long nsec = (x->tv_usec - y->tv_usec) / 1000000; + y->tv_usec += 1000000 * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + tv_usec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_usec = x->tv_usec - y->tv_usec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +/** + * Subtracting two struct timespec values. + * + * Taken from glibc manual. + * + * Subtract the `struct timespec' values X and Y, + * storing the result in RESULT. + * Return 1 if the difference is negative, otherwise 0. + */ +static inline int +timespec_subtract(struct timespec *result, + struct timespec *x, + struct timespec *y) { + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_nsec < y->tv_nsec) { + long nsec = (y->tv_nsec - x->tv_nsec) / NS_PER_SEC + 1; + y->tv_nsec -= NS_PER_SEC * nsec; + y->tv_sec += nsec; + } + + if (x->tv_nsec - y->tv_nsec > NS_PER_SEC) { + long nsec = (x->tv_nsec - y->tv_nsec) / NS_PER_SEC; + y->tv_nsec += NS_PER_SEC * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + tv_nsec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_nsec = x->tv_nsec - y->tv_nsec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +/** + * Get current time in struct timeval. + */ +static inline struct timeval __attribute__((const)) +get_time_timeval(void) { + struct timeval tv = { 0, 0 }; + + gettimeofday(&tv, NULL); + + // Return a time of all 0 if the call fails + return tv; +} + +/** + * Get current time in struct timespec. + * + * Note its starting time is unspecified. + */ +static inline struct timespec __attribute__((const)) +get_time_timespec(void) { + struct timespec tm = { 0, 0 }; + + clock_gettime(CLOCK_MONOTONIC, &tm); + + // Return a time of all 0 if the call fails + return tm; +} + + +/** + * Print time passed since program starts execution. + * + * Used for debugging. + */ +static inline void +print_timestamp(session_t *ps) { + struct timeval tm, diff; + + if (gettimeofday(&tm, NULL)) return; + + timeval_subtract(&diff, &tm, &ps->time_start); + printf("[ %5ld.%02ld ] ", diff.tv_sec, diff.tv_usec / 10000); +} + +/** + * Allocate the space and copy a string. + */ +static inline char * __attribute__((const)) +mstrcpy(const char *src) { + char *str = malloc(sizeof(char) * (strlen(src) + 1)); + + strcpy(str, src); + + return str; +} + +/** + * Allocate the space and copy a string. + */ +static inline char * __attribute__((const)) +mstrncpy(const char *src, unsigned len) { + char *str = malloc(sizeof(char) * (len + 1)); + + strncpy(str, src, len); + + return str; +} + +/** + * Allocate the space and join two strings. + */ +static inline char * __attribute__((const)) +mstrjoin(const char *src1, const char *src2) { + char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + 1)); + + strcpy(str, src1); + strcat(str, src2); + + return str; +} + +/** + * Allocate the space and join two strings; + */ +static inline char * __attribute__((const)) +mstrjoin3(const char *src1, const char *src2, const char *src3) { + char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + + strlen(src3) + 1)); + + strcpy(str, src1); + strcat(str, src2); + strcat(str, src3); + + return str; +} + +/** + * Normalize an int value to a specific range. + * + * @param i int value to normalize + * @param min minimal value + * @param max maximum value + * @return normalized value + */ +static inline int __attribute__((const)) +normalize_i_range(int i, int min, int max) { + if (i > max) return max; + if (i < min) return min; + return i; +} + +/** + * Select the larger integer of two. + */ +static inline int __attribute__((const)) +max_i(int a, int b) { + return (a > b ? a : b); +} + +/** + * Select the smaller integer of two. + */ +static inline int __attribute__((const)) +min_i(int a, int b) { + return (a > b ? b : a); +} + +/** + * Select the larger long integer of two. + */ +static inline long __attribute__((const)) +max_l(long a, long b) { + return (a > b ? a : b); +} + +/** + * Select the smaller long integer of two. + */ +static inline long __attribute__((const)) +min_l(long a, long b) { + return (a > b ? b : a); +} + +/** + * Normalize a double value to a specific range. + * + * @param d double value to normalize + * @param min minimal value + * @param max maximum value + * @return normalized value + */ +static inline double __attribute__((const)) +normalize_d_range(double d, double min, double max) { + if (d > max) return max; + if (d < min) return min; + return d; +} + +/** + * Normalize a double value to 0.\ 0 - 1.\ 0. + * + * @param d double value to normalize + * @return normalized value + */ +static inline double __attribute__((const)) +normalize_d(double d) { + return normalize_d_range(d, 0.0, 1.0); +} + +timeout_t * +timeout_insert(session_t *ps, time_ms_t interval, + bool (*callback)(session_t *ps, timeout_t *ptmout), void *data); + +void +timeout_invoke(session_t *ps, timeout_t *ptmout); + +bool +timeout_drop(session_t *ps, timeout_t *prm); + +/** + * Add a file descriptor to a select() fd_set. + */ +static inline bool +fds_insert_select(fd_set **ppfds, int fd) { + assert(fd <= FD_SETSIZE); + + if (!*ppfds) { + if ((*ppfds = malloc(sizeof(fd_set)))) { + FD_ZERO(*ppfds); + } + else { + fprintf(stderr, "Failed to allocate memory for select() fdset.\n"); + exit(1); + } + } + + FD_SET(fd, *ppfds); + + return true; +} + +/** + * Add a new file descriptor to wait for. + */ +static inline bool +fds_insert(session_t *ps, int fd, short events) { + bool result = true; + + ps->nfds_max = max_i(fd + 1, ps->nfds_max); + + if (POLLIN & events) + result = fds_insert_select(&ps->pfds_read, fd) && result; + if (POLLOUT & events) + result = fds_insert_select(&ps->pfds_write, fd) && result; + if (POLLPRI & events) + result = fds_insert_select(&ps->pfds_except, fd) && result; + + return result; +} + +/** + * Delete a file descriptor to wait for. + */ +static inline void +fds_drop(session_t *ps, int fd, short events) { + // Drop fd from respective fd_set-s + if (POLLIN & events && ps->pfds_read) + FD_CLR(fd, ps->pfds_read); + if (POLLOUT & events && ps->pfds_write) + FD_CLR(fd, ps->pfds_write); + if (POLLPRI & events && ps->pfds_except) + FD_CLR(fd, ps->pfds_except); +} + +#define CPY_FDS(key) \ + fd_set * key = NULL; \ + if (ps->key) { \ + key = malloc(sizeof(fd_set)); \ + memcpy(key, ps->key, sizeof(fd_set)); \ + if (!key) { \ + fprintf(stderr, "Failed to allocate memory for copying select() fdset.\n"); \ + exit(1); \ + } \ + } \ + +/** + * Poll for changes. + * + * poll() is much better than select(), but ppoll() does not exist on + * *BSD. + */ +static inline int +fds_poll(session_t *ps, struct timeval *ptv) { + // Copy fds + CPY_FDS(pfds_read); + CPY_FDS(pfds_write); + CPY_FDS(pfds_except); + + int ret = select(ps->nfds_max, pfds_read, pfds_write, pfds_except, ptv); + + free(pfds_read); + free(pfds_write); + free(pfds_except); + + return ret; +} +#undef CPY_FDS + +/** + * Find a window from window id in window linked list of the session. + */ +static inline win * +find_win(session_t *ps, Window id) { + if (!id) + return NULL; + + win *w; + + for (w = ps->list; w; w = w->next) { + if (w->id == id && !w->destroyed) + return w; + } + + return 0; +} + +/** + * Find out the WM frame of a client window using existing data. + * + * @param id window ID + * @return struct _win object of the found window, NULL if not found + */ +static inline win * +find_toplevel(session_t *ps, Window id) { + if (!id) + return NULL; + + for (win *w = ps->list; w; w = w->next) { + if (w->client_win == id && !w->destroyed) + return w; + } + + return NULL; +} + +/** + * Find out the currently focused window. + * + * @return struct _win object of the found window, NULL if not found + */ +static inline win * +find_focused(session_t *ps) { + if (!ps->o.track_focus) + return NULL; + + for (win *w = ps->list; w; w = w->next) { + if (w->focused_real && !w->destroyed) + return w; + } + + return NULL; +} + +/** + * Copies a region + */ +static inline XserverRegion +copy_region(const session_t *ps, XserverRegion oldregion) { + XserverRegion region = XFixesCreateRegion(ps->dpy, NULL, 0); + + XFixesCopyRegion(ps->dpy, region, oldregion); + + return region; +} + +#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); +//!@} + +/** @name DBus hooks + */ +///@{ +void +win_set_shadow_force(session_t *ps, win *w, switch_t val); + +void +win_set_focused_force(session_t *ps, win *w, switch_t val); + +void +win_set_invert_color_force(session_t *ps, win *w, switch_t val); + +void +force_repaint(session_t *ps); +//!@} +#endif diff --git a/src/compton.c b/src/compton.c index f933d43..90133d1 100644 --- a/src/compton.c +++ b/src/compton.c @@ -3,7 +3,7 @@ * * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * - * Copyright (c) 2011, Christopher Jeffrey + * Copyright (c) 2011-2013, Christopher Jeffrey * See LICENSE for more information. * */ @@ -837,7 +837,7 @@ determine_evmask(session_t *ps, Window wid, win_evmode_t mode) { // Check if it's a mapped client window if (WIN_EVMODE_CLIENT == mode || ((w = find_toplevel(ps, wid)) && IsViewable == w->a.map_state)) { - if (ps->o.frame_opacity || ps->o.track_wdata + if (ps->o.frame_opacity || ps->o.track_wdata || ps->track_atom_lst || ps->o.detect_client_opacity) evmask |= PropertyChangeMask; } @@ -1361,8 +1361,7 @@ paint_preprocess(session_t *ps, win *list) { // Disable unredirection for multi-screen setups if (WMODE_SOLID == w->mode && (!w->frame_opacity || !win_has_frame(w)) - && win_is_fullscreen(ps, w) - && ScreenCount(ps->dpy) <= 1) + && win_is_fullscreen(ps, w)) ps->unredir_possible = true; } @@ -1848,6 +1847,9 @@ add_damage(session_t *ps, XserverRegion damage) { static void repair_win(session_t *ps, win *w) { + if (IsViewable != w->a.map_state) + return; + XserverRegion parts; if (!w->damaged) { @@ -1986,6 +1988,13 @@ map_win(session_t *ps, Window id) { if (w->need_configure) { configure_win(ps, &w->queue_configure); } + +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + cdbus_ev_win_mapped(ps, w); + } +#endif } static void @@ -2042,6 +2051,13 @@ unmap_win(session_t *ps, Window id) { // don't care about properties anymore win_ev_stop(ps, w); + +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + cdbus_ev_win_unmapped(ps, w); + } +#endif } static opacity_t @@ -2583,8 +2599,8 @@ add_win(session_t *ps, Window id, Window prev) { // Fill structure new->id = id; - set_ignore_next(ps); + set_ignore_next(ps); if (!XGetWindowAttributes(ps->dpy, id, &new->a)) { // Failed to get window attributes. Which probably means, the window // is gone already. @@ -2608,7 +2624,14 @@ add_win(session_t *ps, Window id, Window prev) { new->next = *p; *p = new; - if (map_state == IsViewable) { +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + cdbus_ev_win_added(ps, new); + } +#endif + + if (IsViewable == map_state) { map_win(ps, id); } @@ -2811,6 +2834,13 @@ destroy_win(session_t *ps, Window id) { // Fading out the window w->flags |= WFLAG_OPCT_CHANGE; set_fade_callback(ps, w, destroy_callback, false); + +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + cdbus_ev_win_destroyed(ps, w); + } +#endif } } @@ -3252,6 +3282,58 @@ win_get_class(session_t *ps, win *w) { return true; } +#ifdef CONFIG_DBUS +/** @name DBus hooks + */ +///@{ + +/** + * Set w->shadow_force of a window. + */ +void +win_set_shadow_force(session_t *ps, win *w, switch_t val) { + if (val != w->shadow_force) { + w->shadow_force = val; + win_determine_shadow(ps, w); + } +} + +/** + * Set w->focused_force of a window. + */ +void +win_set_focused_force(session_t *ps, win *w, switch_t val) { + if (val != w->focused_force) { + w->focused_force = val; + win_update_focused(ps, w); + } +} + +/** + * Set w->invert_color_force of a window. + */ +void +win_set_invert_color_force(session_t *ps, win *w, switch_t val) { + if (val != w->invert_color_force) { + w->invert_color_force = val; + win_determine_invert_color(ps, w); + } +} + +/** + * Force a full-screen repaint. + */ +void +force_repaint(session_t *ps) { + XserverRegion reg = None; + if (ps->screen_reg && (reg = copy_region(ps, ps->screen_reg))) { + ps->ev_received = true; + add_damage(ps, reg); + } +} +//!@} +#endif + #ifdef DEBUG_EVENTS static int ev_serial(XEvent *ev) { @@ -3647,6 +3729,17 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { } } + // Check for other atoms we are tracking + for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) { + if (platom->atom == ev->atom) { + win *w = find_win(ps, ev->window); + if (!w) + w = find_toplevel(ps, ev->window); + if (w) + win_on_wdata_change(ps, w); + break; + } + } } inline static void @@ -5129,8 +5222,7 @@ timeout_get_poll_time(session_t *ps) { // Traverse throught the timeout linked list for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = ptmout->next) { if (ptmout->enabled) { - // Truncate the last run time to the closest interval - time_ms_t newrun = ptmout->firstrun + ((ptmout->lastrun - ptmout->firstrun) / ptmout->interval + 1) * ptmout->interval; + time_ms_t newrun = timeout_get_newrun(ptmout); if (newrun <= now) { wait = 0; break; @@ -5149,7 +5241,7 @@ timeout_get_poll_time(session_t *ps) { /** * Insert a new timeout. */ -static timeout_t * +timeout_t * timeout_insert(session_t *ps, time_ms_t interval, bool (*callback)(session_t *ps, timeout_t *ptmout), void *data) { const static timeout_t tmout_def = { @@ -5184,7 +5276,7 @@ timeout_insert(session_t *ps, time_ms_t interval, * @return true if we have found the timeout and removed it, false * otherwise */ -static bool +bool timeout_drop(session_t *ps, timeout_t *prm) { timeout_t **pplast = &ps->tmout_lst; @@ -5224,12 +5316,14 @@ static bool timeout_run(session_t *ps) { const time_ms_t now = get_time_ms(); bool ret = false; + timeout_t *pnext = NULL; - for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = ptmout->next) { + for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = pnext) { + pnext = ptmout->next; if (ptmout->enabled) { const time_ms_t max = now + (time_ms_t) (ptmout->interval * TIMEOUT_RUN_TOLERANCE); - time_ms_t newrun = ptmout->firstrun + ((ptmout->lastrun - ptmout->firstrun) / ptmout->interval + 1) * ptmout->interval; + time_ms_t newrun = timeout_get_newrun(ptmout); if (newrun <= max) { ret = true; timeout_invoke(ps, ptmout); @@ -5243,7 +5337,7 @@ timeout_run(session_t *ps) { /** * Invoke a timeout. */ -static void +void timeout_invoke(session_t *ps, timeout_t *ptmout) { const time_ms_t now = get_time_ms(); ptmout->lastrun = now; @@ -5301,6 +5395,12 @@ mainloop(session_t *ps) { return true; } +#ifdef CONFIG_DBUS + if (ps->o.dbus) { + cdbus_loop(ps); + } +#endif + if (ps->reset) return false; @@ -5531,7 +5631,13 @@ session_init(session_t *ps_old, int argc, char **argv) { .atom_ewmh_active_win = None, .atom_compton_shadow = None, .atom_win_type = None, - .atoms_wintypes = { 0 } + .atoms_wintypes = { 0 }, + .track_atom_lst = NULL, + +#ifdef CONFIG_DBUS + .dbus_conn = NULL, + .dbus_service = NULL, +#endif }; // Allocate a session and copy default values into it @@ -5740,6 +5846,19 @@ session_init(session_t *ps_old, int argc, char **argv) { XUngrabServer(ps->dpy); + // Initialize DBus + if (ps->o.dbus) { +#ifdef CONFIG_DBUS + cdbus_init(ps); + if (!ps->dbus_conn) { + cdbus_destroy(ps); + ps->o.dbus = false; + } +#else + printf_errfq(1, "(): DBus support not compiled in!"); +#endif + } + // Fork to background, if asked if (ps->o.fork_after_register) { if (!fork_after(ps)) { @@ -5770,6 +5889,14 @@ session_destroy(session_t *ps) { // Stop listening to events on root window XSelectInput(ps->dpy, ps->root, 0); +#ifdef CONFIG_DBUS + // Kill DBus connection + if (ps->o.dbus) + cdbus_destroy(ps); + + free(ps->dbus_service); +#endif + // Free window linked list { win *next = NULL; diff --git a/src/compton.h b/src/compton.h index 2154f86..c309910 100644 --- a/src/compton.h +++ b/src/compton.h @@ -4,92 +4,20 @@ // Throw everything in here. -// === Options === - -// Debug options, enable them using -D in CFLAGS -// #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 MONITOR_REPAINT 1 - -// Whether to enable PCRE regular expression support in blacklists, enabled -// by default -// #define CONFIG_REGEX_PCRE 1 -// Whether to enable JIT support of libpcre. This may cause problems on PaX -// kernels. -// #define CONFIG_REGEX_PCRE_JIT 1 -// Whether to enable parsing of configuration files using libconfig -// #define CONFIG_LIBCONFIG 1 -// Whether to enable DRM VSync support -// #define CONFIG_VSYNC_DRM 1 -// Whether to enable OpenGL VSync support -// #define CONFIG_VSYNC_OPENGL 1 // === Includes === -// For some special functions -#define _GNU_SOURCE +#include "common.h" -#include -#include -#include -#include #include #include -#include -#include -#include #include #include #include -#include #include -#include #include #include -// libpcre -#ifdef CONFIG_REGEX_PCRE -#include - -// For compatiblity with -#include -#endif - -#ifdef CONFIG_DBUS -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #ifdef CONFIG_VSYNC_DRM #include // We references some definitions in drm.h, which could also be found in @@ -100,776 +28,6 @@ #include #endif -#ifdef CONFIG_VSYNC_OPENGL -#include -#endif - -// === Constants === -#if !(COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2) -#error libXcomposite version unsupported -#endif - -#define ROUNDED_PERCENT 0.05 -#define ROUNDED_PIXELS 10 - -#define OPAQUE 0xffffffff -#define REGISTER_PROP "_NET_WM_CM_S" - -#define TIME_MS_MAX LONG_MAX -#define FADE_DELTA_TOLERANCE 0.2 -#define SWOPTI_TOLERANCE 3000 -#define TIMEOUT_RUN_TOLERANCE 0.2 -#define WIN_GET_LEADER_MAX_RECURSION 20 - -#define SEC_WRAP (15L * 24L * 60L * 60L) - -#define NS_PER_SEC 1000000000L -#define US_PER_SEC 1000000L -#define MS_PER_SEC 1000 - -#define XRFILTER_CONVOLUTION "convolution" -#define XRFILTER_GUASSIAN "gaussian" -#define XRFILTER_BINOMIAL "binomial" - -// Window flags - -// Window size is changed -#define WFLAG_SIZE_CHANGE 0x0001 -// Window size/position is changed -#define WFLAG_POS_CHANGE 0x0002 -// Window opacity / dim state changed -#define WFLAG_OPCT_CHANGE 0x0004 - -// === Macros === - -// #define MSTR_(s) #s -// #define MSTR(s) MSTR_(s) - -/// 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 - -// === Types === - -typedef uint32_t opacity_t; -typedef long time_ms_t; - -typedef enum { - WINTYPE_UNKNOWN, - WINTYPE_DESKTOP, - WINTYPE_DOCK, - WINTYPE_TOOLBAR, - WINTYPE_MENU, - WINTYPE_UTILITY, - WINTYPE_SPLASH, - WINTYPE_DIALOG, - WINTYPE_NORMAL, - WINTYPE_DROPDOWN_MENU, - WINTYPE_POPUP_MENU, - WINTYPE_TOOLTIP, - WINTYPE_NOTIFY, - WINTYPE_COMBO, - WINTYPE_DND, - NUM_WINTYPES -} wintype_t; - -/// Enumeration type to represent switches. -typedef enum { - OFF, // false - ON, // true - UNSET -} switch_t; - -/// Enumeration type of window painting mode. -typedef enum { - WMODE_TRANS, - WMODE_SOLID, - WMODE_ARGB -} winmode_t; - -/// Structure representing needed window updates. -typedef struct { - bool shadow : 1; - bool fade : 1; - bool focus : 1; - bool invert_color : 1; -} win_upd_t; - -/// Structure representing Window property value. -typedef struct { - // All pointers have the same length, right? - // I wanted to use anonymous union but it's a GNU extension... - union { - unsigned char *p8; - short *p16; - long *p32; - } data; - unsigned long nitems; - Atom type; - int format; -} winprop_t; - -typedef struct _ignore { - struct _ignore *next; - unsigned long sequence; -} ignore_t; - -enum wincond_target { - CONDTGT_NAME, - CONDTGT_CLASSI, - CONDTGT_CLASSG, - CONDTGT_ROLE, -}; - -enum wincond_type { - CONDTP_EXACT, - CONDTP_ANYWHERE, - CONDTP_FROMSTART, - CONDTP_WILDCARD, - CONDTP_REGEX_PCRE, -}; - -#define CONDF_IGNORECASE 0x0001 - -typedef struct _wincond { - enum wincond_target target; - enum wincond_type type; - char *pattern; -#ifdef CONFIG_REGEX_PCRE - pcre *regex_pcre; - pcre_extra *regex_pcre_extra; -#endif - int16_t flags; - struct _wincond *next; -} wincond_t; - -/// VSync modes. -typedef enum { - VSYNC_NONE, - VSYNC_DRM, - VSYNC_OPENGL, -} vsync_t; - -#ifdef CONFIG_VSYNC_OPENGL -typedef int (*f_WaitVideoSync) (int, int, unsigned *); -typedef int (*f_GetVideoSync) (unsigned *); -#endif - -typedef struct { - int size; - double *data; -} conv; - -struct _timeout_t; - -struct _win; - -/// Structure representing all options. -typedef struct { - // === General === - char *display; - /// 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 fork to background. - bool fork_after_register; - /// Whether to detect rounded corners. - bool detect_rounded_corners; - /// Whether to paint on X Composite overlay window instead of root - /// window. - bool paint_on_overlay; - /// Whether to unredirect all windows if a full-screen opaque window - /// is detected. - bool unredir_if_possible; - /// Whether to enable D-Bus support. - bool dbus; - /// Path to log file. - char *logpath; - /// Whether to work under synchronized mode for debugging. - bool synchronize; - - // === VSync & software optimization === - /// User-specified refresh rate. - int refresh_rate; - /// Whether to enable refresh-rate-based software optimization. - bool sw_opti; - /// VSync method to use; - vsync_t vsync; - /// Whether to enable double buffer. - bool dbe; - /// Whether to do VSync aggressively. - bool vsync_aggressive; - - // === Shadow === - /// Enable/disable shadow for specific window types. - bool wintype_shadow[NUM_WINTYPES]; - /// Red, green and blue tone of the shadow. - double shadow_red, shadow_green, shadow_blue; - int shadow_radius; - int shadow_offset_x, shadow_offset_y; - double shadow_opacity; - bool clear_shadow; - /// Shadow blacklist. A linked list of conditions. - wincond_t *shadow_blacklist; - /// Whether bounding-shaped window should be ignored. - bool shadow_ignore_shaped; - /// Whether to respect _COMPTON_SHADOW. - bool respect_prop_shadow; - - // === 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; - /// Fading blacklist. A linked list of conditions. - wincond_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; - /// 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; - /// 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. - wincond_t *invert_color_list; - - // === Focus related === - /// Consider windows of specific types to be always focused. - bool wintype_focus[NUM_WINTYPES]; - /// 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. - wincond_t *focus_blacklist; - /// Whether to do window grouping with WM_TRANSIENT_FOR. - bool detect_transient; - /// Whether to do window grouping with WM_CLIENT_LEADER. - bool detect_client_leader; - - // === Calculated === - /// Whether compton needs to track focus changes. - bool track_focus; - /// Whether compton needs to track window name and class. - bool track_wdata; - /// Whether compton needs to track window leaders. - bool track_leader; -} options_t; - -/// Structure containing all necessary data for a compton session. -typedef struct { - // === Display related === - /// Display in use. - Display *dpy; - /// Default screen. - int scr; - /// Default visual. - Visual *vis; - /// Default depth. - int depth; - /// Root window. - Window root; - /// Height of root window. - int root_height; - /// Width of root window. - int root_width; - // Damage of root window. - // Damage root_damage; - /// X Composite overlay window. Used if --paint-on-overlay. - Window overlay; - /// Picture of the root window background. - Picture root_tile; - /// A region of the size of the screen. - XserverRegion screen_reg; - /// Picture of root window. Destination of painting in no-DBE painting - /// mode. - Picture root_picture; - /// A Picture acting as the painting target. - Picture tgt_picture; - /// Temporary buffer to paint to before sending to display. - Picture tgt_buffer; - /// DBE back buffer for root window. Used in DBE painting mode. - XdbeBackBuffer root_dbe; - /// Window ID of the window we register as a symbol. - Window reg_win; - - // === Operation related === - /// Program options. - options_t o; - /// File descriptors to check for reading. - fd_set *pfds_read; - /// File descriptors to check for writing. - fd_set *pfds_write; - /// File descriptors to check for exceptions. - fd_set *pfds_except; - /// Largest file descriptor in fd_set-s above. - int nfds_max; - /// Linked list of all timeouts. - struct _timeout_t *tmout_lst; - /// Whether we have received an event in this cycle. - bool ev_received; - /// Whether the program is idling. I.e. no fading, no potential window - /// changes. - bool idling; - /// Program start time. - struct timeval time_start; - /// The region needs to painted on next paint. - XserverRegion all_damage; - /// Whether all windows are currently redirected. - bool redirected; - /// Whether there's a highest full-screen window, and all windows could - /// be unredirected. - bool unredir_possible; - /// Pre-generated alpha pictures. - Picture *alpha_picts; - /// Whether all reg_ignore of windows should expire in this paint. - bool reg_ignore_expire; - /// Time of last fading. In milliseconds. - time_ms_t fade_time; - /// Head pointer of the error ignore linked list. - ignore_t *ignore_head; - /// Pointer to the next member of tail element of the error - /// ignore linked list. - ignore_t **ignore_tail; - /// Reset program after next paint. - bool reset; - - // === Expose event related === - /// Pointer to an array of XRectangle-s of exposed region. - XRectangle *expose_rects; - /// Number of XRectangle-s in expose_rects. - int size_expose; - /// Index of the next free slot in expose_rects. - int n_expose; - - // === Window related === - /// Linked list of all windows. - struct _win *list; - /// Pointer to win of current active window. Used by - /// EWMH _NET_ACTIVE_WINDOW focus detection. In theory, - /// it's more reliable to store the window ID directly here, just in - /// case the WM does something extraordinary, but caching the pointer - /// means another layer of complexity. - struct _win *active_win; - /// Window ID of leader window of currently active window. Used for - /// subsidiary window detection. - Window active_leader; - - // === Shadow/dimming related === - /// 1x1 black Picture. - Picture black_picture; - /// 1x1 Picture of the shadow color. - Picture cshadow_picture; - /// 1x1 white Picture. - Picture white_picture; - /// Gaussian map of shadow. - conv *gaussian_map; - // for shadow precomputation - /// Shadow depth on one side. - int cgsize; - /// Pre-computed color table for corners of shadow. - unsigned char *shadow_corner; - /// Pre-computed color table for a side of shadow. - unsigned char *shadow_top; - - // === Software-optimization-related === - /// Currently used refresh rate. - short refresh_rate; - /// Interval between refresh in nanoseconds. - long refresh_intv; - /// Nanosecond offset of the first painting. - long paint_tm_offset; - - #ifdef CONFIG_VSYNC_DRM - // === DRM VSync related === - /// File descriptor of DRI device file. Used for DRM VSync. - int drm_fd; - #endif - - #ifdef CONFIG_VSYNC_OPENGL - // === OpenGL VSync related === - /// GLX context. - GLXContext glx_context; - /// Pointer to glXGetVideoSyncSGI function. - f_GetVideoSync glx_get_video_sync; - /// Pointer to glXWaitVideoSyncSGI function. - f_WaitVideoSync glx_wait_video_sync; - #endif - - // === X extension related === - /// Event base number for X Fixes extension. - int xfixes_event; - /// Error base number for X Fixes extension. - int xfixes_error; - /// Event base number for X Damage extension. - int damage_event; - /// Error base number for X Damage extension. - int damage_error; - /// Event base number for X Render extension. - int render_event; - /// Error base number for X Render extension. - int render_error; - /// Event base number for X Composite extension. - int composite_event; - /// Error base number for X Composite extension. - int composite_error; - /// Major opcode for X Composite extension. - int composite_opcode; - /// Whether X Composite NameWindowPixmap is available. Aka if X - /// Composite version >= 0.2. - bool has_name_pixmap; - /// Whether X Shape extension exists. - bool shape_exists; - /// Event base number for X Shape extension. - int shape_event; - /// Error base number for X Shape extension. - int shape_error; - /// Whether X RandR extension exists. - bool randr_exists; - /// Event base number for X RandR extension. - int randr_event; - /// Error base number for X RandR extension. - int randr_error; - #ifdef CONFIG_VSYNC_OPENGL - /// Whether X GLX extension exists. - bool glx_exists; - /// Event base number for X GLX extension. - int glx_event; - /// Error base number for X GLX extension. - int glx_error; - #endif - /// Whether X DBE extension exists. - bool dbe_exists; - /// Whether X Render convolution filter exists. - bool xrfilter_convolution_exists; - - // === Atoms === - /// Atom of property _NET_WM_OPACITY. - Atom atom_opacity; - /// Atom of _NET_FRAME_EXTENTS. - Atom atom_frame_extents; - /// Property atom to identify top-level frame window. Currently - /// WM_STATE. - Atom atom_client; - /// Atom of property WM_NAME. - Atom atom_name; - /// Atom of property _NET_WM_NAME. - Atom atom_name_ewmh; - /// Atom of property WM_CLASS. - Atom atom_class; - /// Atom of property WM_WINDOW_ROLE. - Atom atom_role; - /// Atom of property WM_TRANSIENT_FOR. - Atom atom_transient; - /// Atom of property WM_CLIENT_LEADER. - Atom atom_client_leader; - /// Atom of property _NET_ACTIVE_WINDOW. - Atom atom_ewmh_active_win; - /// Atom of property _COMPTON_SHADOW. - Atom atom_compton_shadow; - /// Atom of property _NET_WM_WINDOW_TYPE. - Atom atom_win_type; - /// Array of atoms of all possible window types. - Atom atoms_wintypes[NUM_WINTYPES]; -} session_t; - -/// Structure representing a top-level window compton manages. -typedef struct _win { - /// Pointer to the next structure in the linked list. - struct _win *next; - /// Pointer to the next higher window to paint. - struct _win *prev_trans; - - // Core members - /// ID of the top-level frame window. - Window id; - /// Window attributes. - XWindowAttributes a; - /// Window painting mode. - winmode_t mode; - /// Whether the window has been damaged at least once. - bool damaged; - /// Damage of the window. - Damage damage; - /// NameWindowPixmap of the window. - Pixmap pixmap; - /// Picture of the window. - Picture picture; - /// Bounding shape of the window. - XserverRegion border_size; - /// Region of the whole window, shadow region included. - XserverRegion extents; - /// Window flags. Definitions above. - int_fast16_t flags; - /// Whether there's a pending ConfigureNotify happening - /// when the window is unmapped. - bool need_configure; - /// Queued ConfigureNotify when the window is unmapped. - XConfigureEvent queue_configure; - /// Region to be ignored when painting. Basically the region where - /// higher opaque windows will paint upon. Depends on window frame - /// opacity state, window geometry, window mapped/unmapped state, - /// window mode, of this and all higher windows. - XserverRegion reg_ignore; - /// Cached width/height of the window including border. - int widthb, heightb; - /// Whether the window has been destroyed. - bool destroyed; - /// Whether the window is bounding-shaped. - bool bounding_shaped; - /// Whether the window just have rounded corners. - bool rounded_corners; - /// Whether this window is to be painted. - bool to_paint; - /// Whether this window is in open/close state. - bool in_openclose; - - // Client window related members - /// ID of the top-level client window of the window. - Window client_win; - /// Type of the window. - wintype_t window_type; - /// Whether it looks like a WM window. We consider a window WM window if - /// it does not have a decedent with WM_STATE and it is not override- - /// redirected itself. - bool wmwin; - /// Leader window ID of the window. - Window leader; - /// Cached topmost window ID of the window. - Window cache_leader; - - // Focus-related members - /// Whether the window is to be considered focused. - bool focused; - /// Override value of window focus state. Set by D-Bus method calls. - switch_t focused_force; - /// Whether the window is actually focused. - bool focused_real; - - // Blacklist related members - /// Name of the window. - char *name; - /// Window instance class of the window. - char *class_instance; - /// Window general class of the window. - char *class_general; - /// WM_WINDOW_ROLE value of the window. - char *role; - wincond_t *cache_sblst; - wincond_t *cache_fblst; - wincond_t *cache_fcblst; - wincond_t *cache_ivclst; - - // Opacity-related members - /// Current window opacity. - opacity_t opacity; - /// Target window opacity. - opacity_t opacity_tgt; - /// Cached value of opacity window attribute. - opacity_t opacity_prop; - /// Cached value of opacity window attribute on client window. For - /// broken window managers not transferring client window's - /// _NET_WM_OPACITY value - opacity_t opacity_prop_client; - /// Alpha mask Picture to render window with opacity. - Picture alpha_pict; - - // Fading-related members - /// Do not fade if it's false. Change on window type change. - /// Used by fading blacklist in the future. - bool fade; - /// Callback to be called after fading completed. - void (*fade_callback) (session_t *ps, struct _win *w); - - // Frame-opacity-related members - /// Current window frame opacity. Affected by window opacity. - double frame_opacity; - /// Alpha mask Picture to render window frame with opacity. - Picture frame_alpha_pict; - /// Frame widths. Determined by client window attributes. - unsigned int left_width, right_width, top_width, bottom_width; - - // Shadow-related members - /// Whether a window has shadow. Calculated. - bool shadow; - /// Override value of window shadow state. Set by D-Bus method calls. - switch_t shadow_force; - /// Opacity of the shadow. Affected by window opacity and frame opacity. - double shadow_opacity; - /// X offset of shadow. Affected by commandline argument. - int shadow_dx; - /// Y offset of shadow. Affected by commandline argument. - int shadow_dy; - /// Width of shadow. Affected by window size and commandline argument. - int shadow_width; - /// Height of shadow. Affected by window size and commandline argument. - int shadow_height; - /// Picture to render shadow. Affected by window size. - Picture shadow_pict; - /// Alpha mask Picture to render shadow. Affected by shadow opacity. - Picture shadow_alpha_pict; - /// The value of _COMPTON_SHADOW attribute of the window. Below 0 for - /// none. - long prop_shadow; - - // Dim-related members - /// Whether the window is to be dimmed. - bool dim; - /// Picture for dimming. Affected by user-specified inactive dim - /// opacity and window opacity. - Picture dim_alpha_pict; - - /// Whether to invert window color. - bool invert_color; - /// Override value of window color inversion state. Set by D-Bus method - /// calls. - switch_t invert_color_force; -} win; - -/// Temporary structure used for communication between -/// get_cfg() and parse_config(). -struct options_tmp { - bool no_dock_shadow; - bool no_dnd_shadow; - double menu_opacity; -}; - -/// Structure for a recorded timeout. -typedef struct _timeout_t { - bool enabled; - void *data; - bool (*callback)(session_t *ps, struct _timeout_t *ptmout); - time_ms_t interval; - time_ms_t firstrun; - time_ms_t lastrun; - struct _timeout_t *next; -} timeout_t; - -/// Enumeration for window event hints. -typedef enum { - WIN_EVMODE_UNKNOWN, - WIN_EVMODE_FRAME, - WIN_EVMODE_CLIENT -} win_evmode_t; - -extern const char *WINTYPES[NUM_WINTYPES]; -extern session_t *ps_g; - -// == Debugging code == -static void -print_timestamp(session_t *ps); - -#ifdef DEBUG_ALLOC_REG - -#include -#define BACKTRACE_SIZE 5 - -/** - * Print current backtrace, excluding the first two items. - * - * 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 = 2; i < size; i++) - printf ("%s\n", strings[i]); - - free(strings); -} - -/** - * Wrapper of XFixesCreateRegion, for debugging. - */ -static inline XserverRegion -XFixesCreateRegion_(Display *dpy, XRectangle *p, int n, - const char *func, int line) { - XserverRegion reg = XFixesCreateRegion(dpy, p, n); - print_timestamp(ps_g); - printf("%#010lx: XFixesCreateRegion() in %s():%d\n", reg, func, line); - print_backtrace(); - fflush(stdout); - return reg; -} - -/** - * Wrapper of XFixesDestroyRegion, for debugging. - */ -static inline void -XFixesDestroyRegion_(Display *dpy, XserverRegion reg, - const char *func, int line) { - XFixesDestroyRegion(dpy, reg); - print_timestamp(ps_g); - printf("%#010lx: XFixesDestroyRegion() in %s():%d\n", reg, func, line); - fflush(stdout); -} - -#define XFixesCreateRegion(dpy, p, n) XFixesCreateRegion_(dpy, p, n, __func__, __LINE__) -#define XFixesDestroyRegion(dpy, reg) XFixesDestroyRegion_(dpy, reg, __func__, __LINE__) -#endif - // == Functions == // inline functions must be made static to compile correctly under clang: @@ -952,111 +110,6 @@ wintype_arr_enable_unset(switch_t arr[]) { arr[i] = ON; } -/** - * Allocate the space and copy a string. - */ -static inline char * __attribute__((const)) -mstrcpy(const char *src) { - char *str = malloc(sizeof(char) * (strlen(src) + 1)); - - strcpy(str, src); - - return str; -} - -/** - * Allocate the space and join two strings. - */ -static inline char * __attribute__((const)) -mstrjoin(const char *src1, const char *src2) { - char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + 1)); - - strcpy(str, src1); - strcat(str, src2); - - return str; -} - -/** - * Allocate the space and join two strings; - */ -static inline char * __attribute__((const)) -mstrjoin3(const char *src1, const char *src2, const char *src3) { - char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) - + strlen(src3) + 1)); - - strcpy(str, src1); - strcat(str, src2); - strcat(str, src3); - - return str; -} - -/** - * Normalize an int value to a specific range. - * - * @param i int value to normalize - * @param min minimal value - * @param max maximum value - * @return normalized value - */ -static inline int __attribute__((const)) -normalize_i_range(int i, int min, int max) { - if (i > max) return max; - if (i < min) return min; - return i; -} - -/** - * Select the larger integer of two. - */ -static inline int __attribute__((const)) -max_i(int a, int b) { - return (a > b ? a : b); -} - -/** - * Select the smaller integer of two. - */ -static inline int __attribute__((const)) -min_i(int a, int b) { - return (a > b ? b : a); -} - -/** - * Select the smaller long integer of two. - */ -static inline long __attribute__((const)) -min_l(long a, long b) { - return (a > b ? b : a); -} - -/** - * Normalize a double value to a specific range. - * - * @param d double value to normalize - * @param min minimal value - * @param max maximum value - * @return normalized value - */ -static inline double __attribute__((const)) -normalize_d_range(double d, double min, double max) { - if (d > max) return max; - if (d < min) return min; - return d; -} - -/** - * Normalize a double value to 0.\ 0 - 1.\ 0. - * - * @param d double value to normalize - * @return normalized value - */ -static inline double __attribute__((const)) -normalize_d(double d) { - return normalize_d_range(d, 0.0, 1.0); -} - /** * Check if a window ID exists in an array of window IDs. * @@ -1074,160 +127,6 @@ array_wid_exists(const Window *arr, int count, Window wid) { return false; } - -/** - * Return whether a struct timeval value is empty. - */ -static inline bool -timeval_isempty(struct timeval *ptv) { - if (!ptv) - return false; - - return ptv->tv_sec <= 0 && ptv->tv_usec <= 0; -} - -/** - * Compare a struct timeval with a time in milliseconds. - * - * @return > 0 if ptv > ms, 0 if ptv == 0, -1 if ptv < ms - */ -static inline int -timeval_ms_cmp(struct timeval *ptv, time_ms_t ms) { - assert(ptv); - - // We use those if statement instead of a - expression because of possible - // truncation problem from long to int. - { - long sec = ms / MS_PER_SEC; - if (ptv->tv_sec > sec) - return 1; - if (ptv->tv_sec < sec) - return -1; - } - - { - long usec = ms % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC); - if (ptv->tv_usec > usec) - return 1; - if (ptv->tv_usec < usec) - return -1; - } - - return 0; -} - -/* - * Subtracting two struct timeval values. - * - * Taken from glibc manual. - * - * Subtract the `struct timeval' values X and Y, - * storing the result in RESULT. - * Return 1 if the difference is negative, otherwise 0. */ -static int -timeval_subtract(struct timeval *result, - struct timeval *x, - struct timeval *y) { - /* Perform the carry for the later subtraction by updating y. */ - if (x->tv_usec < y->tv_usec) { - long nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; - y->tv_usec -= 1000000 * nsec; - y->tv_sec += nsec; - } - - if (x->tv_usec - y->tv_usec > 1000000) { - long nsec = (x->tv_usec - y->tv_usec) / 1000000; - y->tv_usec += 1000000 * nsec; - y->tv_sec -= nsec; - } - - /* Compute the time remaining to wait. - tv_usec is certainly positive. */ - result->tv_sec = x->tv_sec - y->tv_sec; - result->tv_usec = x->tv_usec - y->tv_usec; - - /* Return 1 if result is negative. */ - return x->tv_sec < y->tv_sec; -} - -/* - * Subtracting two struct timespec values. - * - * Taken from glibc manual. - * - * Subtract the `struct timespec' values X and Y, - * storing the result in RESULT. - * Return 1 if the difference is negative, otherwise 0. - */ -static inline int -timespec_subtract(struct timespec *result, - struct timespec *x, - struct timespec *y) { - /* Perform the carry for the later subtraction by updating y. */ - if (x->tv_nsec < y->tv_nsec) { - long nsec = (y->tv_nsec - x->tv_nsec) / NS_PER_SEC + 1; - y->tv_nsec -= NS_PER_SEC * nsec; - y->tv_sec += nsec; - } - - if (x->tv_nsec - y->tv_nsec > NS_PER_SEC) { - long nsec = (x->tv_nsec - y->tv_nsec) / NS_PER_SEC; - y->tv_nsec += NS_PER_SEC * nsec; - y->tv_sec -= nsec; - } - - /* Compute the time remaining to wait. - tv_nsec is certainly positive. */ - result->tv_sec = x->tv_sec - y->tv_sec; - result->tv_nsec = x->tv_nsec - y->tv_nsec; - - /* Return 1 if result is negative. */ - return x->tv_sec < y->tv_sec; -} - -/** - * Get current time in struct timeval. - */ -static inline struct timeval __attribute__((const)) -get_time_timeval(void) { - struct timeval tv = { 0, 0 }; - - gettimeofday(&tv, NULL); - - // Return a time of all 0 if the call fails - return tv; -} - -/** - * Get current time in struct timespec. - * - * Note its starting time is unspecified. - */ -static inline struct timespec __attribute__((const)) -get_time_timespec(void) { - struct timespec tm = { 0, 0 }; - - clock_gettime(CLOCK_MONOTONIC, &tm); - - // Return a time of all 0 if the call fails - return tm; -} - -/** - * Print time passed since program starts execution. - * - * Used for debugging. - */ -static void -print_timestamp(session_t *ps) { - struct timeval tm, diff; - - if (gettimeofday(&tm, NULL)) return; - - timeval_subtract(&diff, &tm, &ps->time_start); - printf("[ %5ld.%02ld ] ", diff.tv_sec, diff.tv_usec / 10000); -} - /** * Destroy a XserverRegion. */ @@ -1347,95 +246,6 @@ ms_to_tv(int timeout) { }; } -/** - * Add a file descriptor to a select() fd_set. - */ -static inline bool -fds_insert_select(fd_set **ppfds, int fd) { - assert(fd <= FD_SETSIZE); - - if (!*ppfds) { - if ((*ppfds = malloc(sizeof(fd_set)))) { - FD_ZERO(*ppfds); - } - else { - fprintf(stderr, "Failed to allocate memory for select() fdset.\n"); - exit(1); - } - } - - FD_SET(fd, *ppfds); - - return true; -} - -/** - * Add a new file descriptor to wait for. - */ -static inline bool -fds_insert(session_t *ps, int fd, short events) { - bool result = true; - - ps->nfds_max = max_i(fd + 1, ps->nfds_max); - - if (POLLIN & events) - result = fds_insert_select(&ps->pfds_read, fd) && result; - if (POLLOUT & events) - result = fds_insert_select(&ps->pfds_write, fd) && result; - if (POLLPRI & events) - result = fds_insert_select(&ps->pfds_except, fd) && result; - - return result; -} - -/** - * Delete a file descriptor to wait for. - */ -static inline void -fds_drop(session_t *ps, int fd, short events) { - // Drop fd from respective fd_set-s - if (POLLIN & events) - FD_CLR(fd, ps->pfds_read); - if (POLLOUT & events) - FD_CLR(fd, ps->pfds_write); - if (POLLPRI & events) - FD_CLR(fd, ps->pfds_except); -} - -#define CPY_FDS(key) \ - fd_set * key = NULL; \ - if (ps->key) { \ - key = malloc(sizeof(fd_set)); \ - memcpy(key, ps->key, sizeof(fd_set)); \ - if (!key) { \ - fprintf(stderr, "Failed to allocate memory for copying select() fdset.\n"); \ - exit(1); \ - } \ - } \ - -/** - * Poll for changes. - * - * poll() is much better than select(), but ppoll() does not exist on - * *BSD. - */ -static inline int -fds_poll(session_t *ps, struct timeval *ptv) { - // Copy fds - CPY_FDS(pfds_read); - CPY_FDS(pfds_write); - CPY_FDS(pfds_except); - - int ret = select(ps->nfds_max, pfds_read, pfds_write, pfds_except, ptv); - - free(pfds_read); - free(pfds_write); - free(pfds_except); - - return ret; -} -#undef CPY_FDS - static void run_fade(session_t *ps, win *w, unsigned steps); @@ -1722,43 +532,6 @@ condlst_add(wincond_t **pcondlst, const char *pattern); static long determine_evmask(session_t *ps, Window wid, win_evmode_t mode); -/** - * Find a window from window id in window linked list of the session. - */ -static inline win * -find_win(session_t *ps, Window id) { - if (!id) - return NULL; - - win *w; - - for (w = ps->list; w; w = w->next) { - if (w->id == id && !w->destroyed) - return w; - } - - return 0; -} - -/** - * Find out the WM frame of a client window using existing data. - * - * @param w window ID - * @return struct _win object of the found window, NULL if not found - */ -static inline win * -find_toplevel(session_t *ps, Window id) { - if (!id) - return NULL; - - for (win *w = ps->list; w; w = w->next) { - if (w->client_win == id && !w->destroyed) - return w; - } - - return NULL; -} - /** * Clear leader cache of all windows. */ @@ -2112,18 +885,6 @@ get_screen_region(session_t *ps) { return XFixesCreateRegion(ps->dpy, &r, 1); } -/** - * Copies a region - */ -inline static XserverRegion -copy_region(const session_t *ps, XserverRegion oldregion) { - XserverRegion region = XFixesCreateRegion(ps->dpy, NULL, 0); - - XFixesCopyRegion(ps->dpy, region, oldregion); - - return region; -} - /** * Dump a region. */ @@ -2281,19 +1042,14 @@ redir_start(session_t *ps); static void redir_stop(session_t *ps); +static inline time_ms_t +timeout_get_newrun(const timeout_t *ptmout) { + return ptmout->firstrun + (max_l((ptmout->lastrun + (long) (ptmout->interval * TIMEOUT_RUN_TOLERANCE) - ptmout->firstrun) / ptmout->interval, (ptmout->lastrun + ptmout->interval * (1 - TIMEOUT_RUN_TOLERANCE) - ptmout->firstrun) / ptmout->interval) + 1) * ptmout->interval; +} + static time_ms_t timeout_get_poll_time(session_t *ps); -static timeout_t * -timeout_insert(session_t *ps, time_ms_t interval, - bool (*callback)(session_t *ps, timeout_t *ptmout), void *data); - -static void -timeout_invoke(session_t *ps, timeout_t *ptmout); - -static bool -timeout_drop(session_t *ps, timeout_t *prm); - static void timeout_clear(session_t *ps); diff --git a/src/dbus.c b/src/dbus.c new file mode 100644 index 0000000..16be7cc --- /dev/null +++ b/src/dbus.c @@ -0,0 +1,920 @@ +/* + * Compton - a compositor for X11 + * + * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard + * + * Copyright (c) 2011-2013, Christopher Jeffrey + * See LICENSE for more information. + * + */ + +#include "dbus.h" + +/** + * Initialize D-Bus connection. + */ +bool +cdbus_init(session_t *ps) { + DBusError err = { }; + + // Initialize + dbus_error_init(&err); + + // Connect to D-Bus + // Use dbus_bus_get_private() so we can fully recycle it ourselves + ps->dbus_conn = dbus_bus_get_private(DBUS_BUS_SESSION, &err); + if (dbus_error_is_set(&err)) { + printf_errf("(): D-Bus connection failed (%s).", err.message); + dbus_error_free(&err); + return false; + } + + if (!ps->dbus_conn) { + printf_errf("(): D-Bus connection failed for unknown reason."); + return false; + } + + // Avoid exiting on disconnect + dbus_connection_set_exit_on_disconnect(ps->dbus_conn, false); + + // Request service name + { + // Get display name + char *display = DisplayString(ps->dpy); + if (!display) + display = "unknown"; + display = mstrcpy(display); + + // Convert all special characters in display name to underscore + { + char *pdisp = display; + + while (*pdisp) { + if (!isalnum(*pdisp)) + *pdisp = '_'; + ++pdisp; + } + } + + // Build service name + char *service = mstrjoin3(CDBUS_SERVICE_NAME, ".", display); + ps->dbus_service = service; + + free(display); + display = NULL; + + // Request for the name + int ret = dbus_bus_request_name(ps->dbus_conn, service, + DBUS_NAME_FLAG_DO_NOT_QUEUE, &err); + + if (dbus_error_is_set(&err)) { + printf_errf("(): Failed to obtain D-Bus name (%s).", err.message); + dbus_error_free(&err); + } + + if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret + && DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER != ret) { + printf_errf("(): Failed to become the primary owner of requested " + "D-Bus name (%d).", ret); + } + } + + + // Add watch handlers + if (!dbus_connection_set_watch_functions(ps->dbus_conn, + cdbus_callback_add_watch, cdbus_callback_remove_watch, + cdbus_callback_watch_toggled, ps, NULL)) { + printf_errf("(): Failed to add D-Bus watch functions."); + return false; + } + + // Add timeout handlers + if (!dbus_connection_set_timeout_functions(ps->dbus_conn, + cdbus_callback_add_timeout, cdbus_callback_remove_timeout, + cdbus_callback_timeout_toggled, ps, NULL)) { + printf_errf("(): Failed to add D-Bus timeout functions."); + return false; + } + + // Add match + dbus_bus_add_match(ps->dbus_conn, + "type='method_call',interface='" CDBUS_INTERFACE_NAME "'", &err); + if (dbus_error_is_set(&err)) { + printf_errf("(): Failed to add D-Bus match."); + dbus_error_free(&err); + return false; + } + + return true; +} + +/** + * Destroy D-Bus connection. + */ +void +cdbus_destroy(session_t *ps) { + if (ps->dbus_conn) { + // Release DBus name firstly + if (ps->dbus_service) { + DBusError err = { }; + dbus_error_init(&err); + + dbus_bus_release_name(ps->dbus_conn, ps->dbus_service, &err); + if (dbus_error_is_set(&err)) { + printf_errf("(): Failed to release DBus name (%s).", + err.message); + dbus_error_free(&err); + } + } + + // Close and unref the connection + dbus_connection_close(ps->dbus_conn); + dbus_connection_unref(ps->dbus_conn); + } +} + +/** @name DBusTimeout handling + */ +///@{ + +/** + * Callback for adding D-Bus timeout. + */ +static dbus_bool_t +cdbus_callback_add_timeout(DBusTimeout *timeout, void *data) { + session_t *ps = data; + + timeout_t *ptmout = timeout_insert(ps, dbus_timeout_get_interval(timeout), + cdbus_callback_handle_timeout, timeout); + if (ptmout) + dbus_timeout_set_data(timeout, ptmout, NULL); + + return (bool) ptmout; +} + +/** + * Callback for removing D-Bus timeout. + */ +static void +cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data) { + session_t *ps = data; + + timeout_t *ptmout = dbus_timeout_get_data(timeout); + assert(ptmout); + if (ptmout) + timeout_drop(ps, ptmout); +} + +/** + * Callback for toggling a D-Bus timeout. + */ +static void +cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data) { + timeout_t *ptmout = dbus_timeout_get_data(timeout); + + assert(ptmout); + if (ptmout) { + ptmout->enabled = dbus_timeout_get_enabled(timeout); + // Refresh interval as libdbus doc says: "Whenever a timeout is toggled, + // its interval may change." + ptmout->interval = dbus_timeout_get_interval(timeout); + } +} + +/** + * Callback for handling a D-Bus timeout. + */ +static bool +cdbus_callback_handle_timeout(session_t *ps, timeout_t *ptmout) { + assert(ptmout && ptmout->data); + if (ptmout && ptmout->data) + return dbus_timeout_handle(ptmout->data); + + return false; +} + +///@} + +/** @name DBusWatch handling + */ +///@{ + +/** + * Callback for adding D-Bus watch. + */ +static dbus_bool_t +cdbus_callback_add_watch(DBusWatch *watch, void *data) { + // Leave disabled watches alone + if (!dbus_watch_get_enabled(watch)) + return TRUE; + + session_t *ps = data; + + // Insert the file descriptor + fds_insert(ps, dbus_watch_get_unix_fd(watch), + cdbus_get_watch_cond(watch)); + + // Always return true + return TRUE; +} + +/** + * Callback for removing D-Bus watch. + */ +static void +cdbus_callback_remove_watch(DBusWatch *watch, void *data) { + session_t *ps = data; + + fds_drop(ps, dbus_watch_get_unix_fd(watch), + cdbus_get_watch_cond(watch)); +} + +/** + * Callback for toggling D-Bus watch status. + */ +static void +cdbus_callback_watch_toggled(DBusWatch *watch, void *data) { + if (dbus_watch_get_enabled(watch)) { + cdbus_callback_add_watch(watch, data); + } + else { + cdbus_callback_remove_watch(watch, data); + } +} + +///@} + +/** @name Message argument appending callbacks + */ +///@{ + +/** + * Callback to append a bool argument to a message. + */ +static bool +cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data) { + assert(data); + + dbus_bool_t val = *(const bool *) data; + + if (!dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &val, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + return false; + } + + return true; +} + +/** + * Callback to append a Window argument to a message. + */ +static bool +cdbus_apdarg_wid(session_t *ps, DBusMessage *msg, const void *data) { + assert(data); + cdbus_window_t val = *(const Window *)data; + + if (!dbus_message_append_args(msg, CDBUS_TYPE_WINDOW, &val, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + return false; + } + + return true; +} + +/** + * Callback to append an cdbus_enum_t argument to a message. + */ +static bool +cdbus_apdarg_enum(session_t *ps, DBusMessage *msg, const void *data) { + assert(data); + if (!dbus_message_append_args(msg, CDBUS_TYPE_ENUM, data, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + return false; + } + + return true; +} + +/** + * Callback to append a string argument to a message. + */ +static bool +cdbus_apdarg_string(session_t *ps, DBusMessage *msg, const void *data) { + const char *str = data; + if (!str) + str = ""; + + if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &str, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + return false; + } + + return true; +} + +/** + * Callback to append all window IDs to a message. + */ +static bool +cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data) { + // Get the number of wids we are to include + unsigned count = 0; + for (win *w = ps->list; w; w = w->next) { + if (!w->destroyed) + ++count; + } + + // Allocate memory for an array of window IDs + cdbus_window_t *arr = malloc(sizeof(cdbus_window_t) * count); + if (!arr) { + printf_errf("(): Failed to allocate memory for window ID array."); + return false; + } + + // Build the array + { + cdbus_window_t *pcur = arr; + for (win *w = ps->list; w; w = w->next) { + if (!w->destroyed) { + *pcur = w->id; + ++pcur; + assert(pcur <= arr + count); + } + } + assert(pcur == arr + count); + } + + // Append arguments + if (!dbus_message_append_args(msg, DBUS_TYPE_ARRAY, CDBUS_TYPE_WINDOW, + &arr, count, DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + free(arr); + return false; + } + + free(arr); + return true; +} +///@} + +/** + * Send a D-Bus signal. + * + * @param ps current session + * @param name signal name + * @param func a function that modifies the built message, to, for example, + * add an argument + * @param data data pointer to pass to the function + */ +static bool +cdbus_signal(session_t *ps, const char *name, + bool (*func)(session_t *ps, DBusMessage *msg, const void *data), + const void *data) { + DBusMessage* msg = NULL; + + // Create a signal + msg = dbus_message_new_signal(CDBUS_OBJECT_NAME, CDBUS_INTERFACE_NAME, + name); + if (!msg) { + printf_errf("(): Failed to create D-Bus signal."); + return false; + } + + // Append arguments onto message + if (func && !func(ps, msg, data)) { + dbus_message_unref(msg); + return false; + } + + // Send the message and flush the connection + if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) { + printf_errf("(): Failed to send D-Bus signal."); + dbus_message_unref(msg); + return false; + } + dbus_connection_flush(ps->dbus_conn); + + // Free the message + dbus_message_unref(msg); + + return true; +} + +/** + * Send a D-Bus reply. + * + * @param ps current session + * @param srcmsg original message + * @param func a function that modifies the built message, to, for example, + * add an argument + * @param data data pointer to pass to the function + */ +static bool +cdbus_reply(session_t *ps, DBusMessage *srcmsg, + bool (*func)(session_t *ps, DBusMessage *msg, const void *data), + const void *data) { + DBusMessage* msg = NULL; + + // Create a reply + msg = dbus_message_new_method_return(srcmsg); + if (!msg) { + printf_errf("(): Failed to create D-Bus reply."); + return false; + } + + // Append arguments onto message + if (func && !func(ps, msg, data)) { + dbus_message_unref(msg); + return false; + } + + // Send the message and flush the connection + if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) { + printf_errf("(): Failed to send D-Bus reply."); + dbus_message_unref(msg); + return false; + } + dbus_connection_flush(ps->dbus_conn); + + // Free the message + dbus_message_unref(msg); + + return true; +} + +/** + * Send a D-Bus error reply. + * + * @param ps current session + * @param msg the new error DBusMessage + */ +static bool +cdbus_reply_errm(session_t *ps, DBusMessage *msg) { + if (!msg) { + printf_errf("(): Failed to create D-Bus reply."); + return false; + } + + // Send the message and flush the connection + if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) { + printf_errf("(): Failed to send D-Bus reply."); + dbus_message_unref(msg); + return false; + } + dbus_connection_flush(ps->dbus_conn); + + // Free the message + dbus_message_unref(msg); + + return true; +} + +/** + * Get n-th argument of a D-Bus message. + * + * @param count the position of the argument to get, starting from 0 + * @param type libdbus type number of the type + * @param pdest pointer to the target + * @return true if successful, false otherwise. + */ +static bool +cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest) { + assert(count >= 0); + + DBusMessageIter iter = { }; + if (!dbus_message_iter_init(msg, &iter)) { + printf_errf("(): Message has no argument."); + return false; + } + + { + const int oldcount = count; + while (count) { + if (!dbus_message_iter_next(&iter)) { + printf_errf("(): Failed to find argument %d.", oldcount); + return false; + } + --count; + } + } + + if (type != dbus_message_iter_get_arg_type(&iter)) { + printf_errf("(): Argument has incorrect type."); + return false; + } + + dbus_message_iter_get_basic(&iter, pdest); + + return true; +} + +void +cdbus_loop(session_t *ps) { + dbus_connection_read_write(ps->dbus_conn, 0); + DBusMessage *msg = NULL; + while ((msg = dbus_connection_pop_message(ps->dbus_conn))) + cdbus_process(ps, msg); +} + +/** @name Message processing + */ +///@{ + +/** + * Process a message from D-Bus. + */ +static void +cdbus_process(session_t *ps, DBusMessage *msg) { + bool success = false; + +#define cdbus_m_ismethod(method) \ + dbus_message_is_method_call(msg, CDBUS_INTERFACE_NAME, method) + + if (cdbus_m_ismethod("reset")) { + ps->reset = true; + if (!dbus_message_get_no_reply(msg)) + cdbus_reply_bool(ps, msg, true); + success = true; + } + else if (cdbus_m_ismethod("list_win")) { + success = cdbus_process_list_win(ps, msg); + } + else if (cdbus_m_ismethod("win_get")) { + success = cdbus_process_win_get(ps, msg); + } + else if (cdbus_m_ismethod("win_set")) { + success = cdbus_process_win_set(ps, msg); + } + else if (cdbus_m_ismethod("find_win")) { + success = cdbus_process_find_win(ps, msg); + } + else if (cdbus_m_ismethod("opts_get")) { + success = cdbus_process_opts_get(ps, msg); + } + else if (cdbus_m_ismethod("opts_set")) { + success = cdbus_process_opts_set(ps, msg); + } +#undef cdbus_m_ismethod + else if (dbus_message_is_method_call(msg, + "org.freedesktop.DBus.Introspectable", "Introspect")) { + success = cdbus_process_introspect(ps, msg); + } + else if (dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameAcquired") + || dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameLost")) { + success = true; + } + else { + if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(msg)) { + printf_errf("(): Error message of path \"%s\" " + "interface \"%s\", member \"%s\", error \"%s\"", + dbus_message_get_path(msg), dbus_message_get_interface(msg), + dbus_message_get_member(msg), dbus_message_get_error_name(msg)); + } + else { + printf_errf("(): Illegal message of type \"%s\", path \"%s\" " + "interface \"%s\", member \"%s\"", + cdbus_repr_msgtype(msg), dbus_message_get_path(msg), + dbus_message_get_interface(msg), dbus_message_get_member(msg)); + } + if (DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg) + && !dbus_message_get_no_reply(msg)) + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADMSG, CDBUS_ERROR_BADMSG_S); + success = true; + } + + // If the message could not be processed, and an reply is expected, return + // an empty reply. + if (!success && DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg) + && !dbus_message_get_no_reply(msg)) + cdbus_reply_err(ps, msg, CDBUS_ERROR_UNKNOWN, CDBUS_ERROR_UNKNOWN_S); + + // Free the message + dbus_message_unref(msg); +} + +/** + * Process a list_win D-Bus request. + */ +static bool +cdbus_process_list_win(session_t *ps, DBusMessage *msg) { + cdbus_reply(ps, msg, cdbus_apdarg_wids, NULL); + + return true; +} + +/** + * Process a win_get D-Bus request. + */ +static bool +cdbus_process_win_get(session_t *ps, DBusMessage *msg) { + cdbus_window_t wid = None; + const char *target = NULL; + DBusError err = { }; + + if (!dbus_message_get_args(msg, &err, + CDBUS_TYPE_WINDOW, &wid, + DBUS_TYPE_STRING, &target, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to parse argument of \"win_get\" (%s).", + err.message); + dbus_error_free(&err); + return false; + } + + win *w = find_win(ps, wid); + + if (!w) { + printf_errf("(): Window %#010x not found.", wid); + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid); + return true; + } + +#define cdbus_m_win_get_do(tgt, apdarg_func) \ + if (!strcmp(MSTR(tgt), target)) { \ + apdarg_func(ps, msg, w->tgt); \ + return true; \ + } + + cdbus_m_win_get_do(client_win, cdbus_reply_wid); + cdbus_m_win_get_do(damaged, cdbus_reply_bool); + cdbus_m_win_get_do(destroyed, cdbus_reply_bool); + cdbus_m_win_get_do(window_type, cdbus_reply_enum); + cdbus_m_win_get_do(wmwin, cdbus_reply_bool); + cdbus_m_win_get_do(leader, cdbus_reply_wid); + cdbus_m_win_get_do(focused_real, cdbus_reply_bool); + cdbus_m_win_get_do(shadow_force, cdbus_reply_enum); + cdbus_m_win_get_do(focused_force, cdbus_reply_enum); + cdbus_m_win_get_do(invert_color_force, cdbus_reply_enum); + if (!strcmp("map_state", target)) { + cdbus_reply_bool(ps, msg, w->a.map_state); + return true; + } +#undef cdbus_m_win_get_do + + printf_errf("(): No matching target found."); + + return false; +} + +/** + * Process a win_set D-Bus request. + */ +static bool +cdbus_process_win_set(session_t *ps, DBusMessage *msg) { + cdbus_window_t wid = None; + const char *target = NULL; + DBusError err = { }; + + if (!dbus_message_get_args(msg, &err, + CDBUS_TYPE_WINDOW, &wid, + DBUS_TYPE_STRING, &target, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to parse argument of \"win_set\" (%s).", + err.message); + dbus_error_free(&err); + return false; + } + + win *w = find_win(ps, wid); + + if (!w) { + printf_errf("(): Window %#010x not found.", wid); + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid); + return true; + } + + ps->ev_received = true; + +#define cdbus_m_win_set_do(tgt, type, real_type) \ + if (!strcmp(MSTR(tgt), target)) { \ + real_type val; \ + if (!cdbus_msg_get_arg(msg, 2, type, &val)) \ + return false; \ + w->tgt = val; \ + goto cdbus_process_win_set_success; \ + } + + if (!strcmp("shadow_force", target)) { + cdbus_enum_t val = UNSET; + if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) + return false; + win_set_shadow_force(ps, w, val); + goto cdbus_process_win_set_success; + } + + if (!strcmp("focused_force", target)) { + cdbus_enum_t val = UNSET; + if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) + return false; + win_set_focused_force(ps, w, val); + goto cdbus_process_win_set_success; + } + + if (!strcmp("invert_color_force", target)) { + cdbus_enum_t val = UNSET; + if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) + return false; + win_set_invert_color_force(ps, w, val); + goto cdbus_process_win_set_success; + } +#undef cdbus_m_win_set_do + + printf_errf("(): No matching target found."); + + return false; + +cdbus_process_win_set_success: + if (!dbus_message_get_no_reply(msg)) + cdbus_reply_bool(ps, msg, true); + return true; +} + +/** + * Process a find_win D-Bus request. + */ +static bool +cdbus_process_find_win(session_t *ps, DBusMessage *msg) { + const char *target = NULL; + + if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) + return false; + + Window wid = None; + + // Find window by client window + if (!strcmp("client", target)) { + cdbus_window_t client = None; + if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_WINDOW, &client)) + return false; + win *w = find_toplevel(ps, client); + if (w) + wid = w->id; + } + // Find focused window + else if (!strcmp("focused", target)) { + win *w = find_focused(ps); + if (w) + wid = w->id; + } + else { + printf_errf("(): No matching target found."); + + return false; + } + + cdbus_reply_wid(ps, msg, wid); + + return true; +} + +/** + * Process a opts_get D-Bus request. + */ +static bool +cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { + const char *target = NULL; + + if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) + return false; + +#define cdbus_m_opts_get_do(tgt, apdarg_func) \ + if (!strcmp(MSTR(tgt), target)) { \ + apdarg_func(ps, msg, ps->o.tgt); \ + return true; \ + } + + cdbus_m_opts_get_do(display, cdbus_reply_string); + cdbus_m_opts_get_do(mark_wmwin_focused, cdbus_reply_bool); + cdbus_m_opts_get_do(mark_ovredir_focused, cdbus_reply_bool); + cdbus_m_opts_get_do(fork_after_register, cdbus_reply_bool); + cdbus_m_opts_get_do(detect_rounded_corners, cdbus_reply_bool); + cdbus_m_opts_get_do(paint_on_overlay, cdbus_reply_bool); + cdbus_m_opts_get_do(unredir_if_possible, cdbus_reply_bool); + cdbus_m_opts_get_do(logpath, cdbus_reply_string); + cdbus_m_opts_get_do(synchronize, cdbus_reply_bool); + + cdbus_m_opts_get_do(clear_shadow, cdbus_reply_bool); +#undef cdbus_m_opts_get_do + + printf_errf("(): No matching target found."); + + return false; +} + +/** + * Process a opts_set D-Bus request. + */ +static bool +cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { + const char *target = NULL; + + if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) + return false; + +#define cdbus_m_opts_set_do(tgt, type, real_type) \ + if (!strcmp(MSTR(tgt), target)) { \ + real_type val; \ + if (!cdbus_msg_get_arg(msg, 1, type, &val)) \ + return false; \ + ps->o.tgt = val; \ + goto cdbus_process_opts_set_success; \ + } + + // unredir_if_possible + if (!strcmp("unredir_if_possible", target)) { + dbus_bool_t val = FALSE; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) + return false; + if (ps->o.unredir_if_possible != val) { + ps->o.unredir_if_possible = val; + ps->ev_received = true; + } + goto cdbus_process_opts_set_success; + } + + // clear_shadow + if (!strcmp("clear_shadow", target)) { + dbus_bool_t val = FALSE; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) + return false; + if (ps->o.clear_shadow != val) { + ps->o.clear_shadow = val; + force_repaint(ps); + } + goto cdbus_process_opts_set_success; + } +#undef cdbus_m_opts_set_do + + printf_errf("(): No matching target found."); + + return false; + +cdbus_process_opts_set_success: + if (!dbus_message_get_no_reply(msg)) + cdbus_reply_bool(ps, msg, true); + return true; +} + +/** + * Process an Introspect D-Bus request. + */ +static bool +cdbus_process_introspect(session_t *ps, DBusMessage *msg) { + const static char *str_introspect = + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; + + cdbus_reply_string(ps, msg, str_introspect); + + return true; +} +///@} + +/** @name Core callbacks + */ +///@{ +void +cdbus_ev_win_added(session_t *ps, win *w) { + if (ps->dbus_conn) + cdbus_signal_wid(ps, "win_added", w->id); +} + +void +cdbus_ev_win_destroyed(session_t *ps, win *w) { + if (ps->dbus_conn) + cdbus_signal_wid(ps, "win_destroyed", w->id); +} + +void +cdbus_ev_win_mapped(session_t *ps, win *w) { + if (ps->dbus_conn) + cdbus_signal_wid(ps, "win_mapped", w->id); +} + +void +cdbus_ev_win_unmapped(session_t *ps, win *w) { + if (ps->dbus_conn) + cdbus_signal_wid(ps, "win_unmapped", w->id); +} +//!@} diff --git a/src/dbus.h b/src/dbus.h new file mode 100644 index 0000000..fb51c8c --- /dev/null +++ b/src/dbus.h @@ -0,0 +1,213 @@ +/* + * Compton - a compositor for X11 + * + * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard + * + * Copyright (c) 2011-2013, Christopher Jeffrey + * See LICENSE for more information. + * + */ + +#include "common.h" +#include + +#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 "Something wrong in arguments?" +#define CDBUS_ERROR_BADWIN CDBUS_ERROR_PREFIX ".bad_window" +#define CDBUS_ERROR_BADWIN_S "Requested window %#010lx not found." +#define CDBUS_ERROR_FORBIDDEN CDBUS_ERROR_PREFIX ".forbidden" +#define CDBUS_ERROR_FORBIDDEN_S "Incorrect password, access denied." + +// Window type +typedef uint32_t cdbus_window_t; +#define CDBUS_TYPE_WINDOW DBUS_TYPE_UINT32 +#define CDBUS_TYPE_WINDOW_STR DBUS_TYPE_UINT32_AS_STRING + +typedef uint16_t cdbus_enum_t; +#define CDBUS_TYPE_ENUM DBUS_TYPE_UINT16 +#define CDBUS_TYPE_ENUM_STR DBUS_TYPE_UINT16_AS_STRING + +static dbus_bool_t +cdbus_callback_add_timeout(DBusTimeout *timeout, void *data); + +static void +cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data); + +static void +cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data); + +static bool +cdbus_callback_handle_timeout(session_t *ps, timeout_t *ptmout); + +/** + * Determine the poll condition of a DBusWatch. + */ +static inline short +cdbus_get_watch_cond(DBusWatch *watch) { + const unsigned flags = dbus_watch_get_flags(watch); + short condition = POLLERR | POLLHUP; + if (flags & DBUS_WATCH_READABLE) + condition |= POLLIN; + if (flags & DBUS_WATCH_WRITABLE) + condition |= POLLOUT; + + return condition; +} + +static dbus_bool_t +cdbus_callback_add_watch(DBusWatch *watch, void *data); + +static void +cdbus_callback_remove_watch(DBusWatch *watch, void *data); + +static void +cdbus_callback_watch_toggled(DBusWatch *watch, void *data); + +static bool +cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data); + +static bool +cdbus_apdarg_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 a wid argument. + */ +static inline bool +cdbus_reply_wid(session_t *ps, DBusMessage *srcmsg, Window wid) { + return cdbus_reply(ps, srcmsg, cdbus_apdarg_wid, &wid); +} + +/** + * Send a reply with a string argument. + */ +static inline bool +cdbus_reply_string(session_t *ps, DBusMessage *srcmsg, const char *str) { + return cdbus_reply(ps, srcmsg, cdbus_apdarg_string, str); +} + +/** + * Send a reply with a enum argument. + */ +static inline bool +cdbus_reply_enum(session_t *ps, DBusMessage *srcmsg, cdbus_enum_t eval) { + return cdbus_reply(ps, srcmsg, cdbus_apdarg_enum, &eval); +} + +///@} + +static bool +cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest); + +/** + * Return a string representation of a D-Bus message type. + */ +static inline const char * +cdbus_repr_msgtype(DBusMessage *msg) { + return dbus_message_type_to_string(dbus_message_get_type(msg)); +} + +/** @name Message processing + */ +///@{ + +static void +cdbus_process(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_list_win(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_win_get(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_win_set(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_find_win(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_opts_get(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_opts_set(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_introspect(session_t *ps, DBusMessage *msg); + +///@}