Bug fix: Issue #37, fix 5 opacity-related bugs

More details in the bug report.

- Rewritten much of the opacity calculation, code cleanup.

- Commandline switch --inactive_opacity_override to restore the old
  behavior in which inactive_opacity has higher priority than
  _NET_WM_OPACITY.
This commit is contained in:
Richard Grenville 2012-09-11 22:22:58 +08:00
parent deaaf5c5cf
commit 129115171a
2 changed files with 154 additions and 54 deletions

View File

@ -81,9 +81,6 @@ Bool win_type_fade[NUM_WINTYPES];
* Macros
*/
#define INACTIVE_OPACITY \
(unsigned long)((double)inactive_opacity * OPAQUE)
#define IS_NORMAL_WIN(w) \
((w) && ((w)->window_type == WINTYPE_NORMAL \
|| (w)->window_type == WINTYPE_UTILITY))
@ -108,8 +105,14 @@ Bool fade_trans = False;
Bool clear_shadow = False;
double inactive_opacity = 0;
double frame_opacity = 0;
/// 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 = 0;
/// Whether inactive_opacity overrides the opacity set by window
/// attributes.
Bool inactive_opacity_override = False;
double frame_opacity = 0.0;
Bool synchronize = False;
@ -203,21 +206,7 @@ set_fade(Display *dpy, win *w, double start,
}
f->callback = callback;
w->opacity = f->cur * OPAQUE;
determine_mode(dpy, w);
if (w->shadow) {
XRenderFreePicture(dpy, w->shadow);
w->shadow = None;
if (w->extents != None) {
XFixesDestroyRegion(dpy, w->extents);
}
/* rebuild the shadow */
w->extents = win_extents(dpy, w);
}
set_opacity(dpy, w, f->cur * OPAQUE);
/* fading windows need to be drawn, mark
them as damaged. when a window maps,
@ -1364,6 +1353,7 @@ map_win(Display *dpy, Window id,
if (!w) return;
w->focused = False;
w->a.map_state = IsViewable;
w->window_type = determine_wintype(dpy, w->id, w->id);
@ -1382,8 +1372,7 @@ map_win(Display *dpy, Window id,
}
}
// this causes problems for inactive transparency
//w->opacity = get_opacity_prop(dpy, w, OPAQUE);
calc_opacity(dpy, w, True);
determine_mode(dpy, w);
@ -1481,8 +1470,7 @@ unmap_win(Display *dpy, Window id, Bool fade) {
finish_unmap_win(dpy, w);
}
static unsigned int
get_opacity_prop(Display *dpy, win *w, unsigned int def) {
opacity_t get_opacity_prop(Display *dpy, win *w, opacity_t def) {
Atom actual;
int format;
unsigned long n, left;
@ -1493,9 +1481,8 @@ get_opacity_prop(Display *dpy, win *w, unsigned int def) {
XA_CARDINAL, &actual, &format, &n, &left, &data);
if (result == Success && data != NULL) {
unsigned int i;
memcpy(&i, data, sizeof(unsigned int));
XFree((void *)data);
opacity_t i = *((opacity_t *) data);
XFree(data);
return i;
}
@ -1504,11 +1491,7 @@ get_opacity_prop(Display *dpy, win *w, unsigned int def) {
static double
get_opacity_percent(Display *dpy, win *w) {
double def = win_type_opacity[w->window_type];
unsigned int opacity =
get_opacity_prop(dpy, w, (unsigned int)(OPAQUE * def));
return opacity * 1.0 / OPAQUE;
return w->opacity * 1.0 / OPAQUE;
}
static void
@ -1558,8 +1541,11 @@ determine_mode(Display *dpy, win *w) {
}
}
static void
set_opacity(Display *dpy, win *w, unsigned long opacity) {
void set_opacity(Display *dpy, win *w, opacity_t opacity) {
// Do nothing if the opacity does not change
if (w->opacity == opacity)
return;
w->opacity = opacity;
determine_mode(dpy, w);
if (w->shadow) {
@ -1575,6 +1561,53 @@ set_opacity(Display *dpy, win *w, unsigned long opacity) {
}
}
/**
* Calculate and set the opacity of a window.
*
* If window is inactive and inactive_opacity_override is set, the
* priority is: (Simulates the old behavior)
*
* inactive_opacity > _NET_WM_WINDOW_OPACITY (if not opaque)
* > window type default opacity
*
* Otherwise:
*
* _NET_WM_WINDOW_OPACITY (if not opaque)
* > window type default opacity (if not opaque)
* > inactive_opacity
*
* @param dpy X display to use
* @param w struct _win object representing the window
* @param refetch_prop whether _NET_WM_OPACITY of the window needs to be
* refetched
*/
void calc_opacity(Display *dpy, win *w, Bool refetch_prop) {
opacity_t opacity;
// Do nothing for unmapped window, calc_opacity() will be called
// when it's mapped
// I suppose I need not to check for IsUnviewable here?
if (IsViewable != w->a.map_state)
return;
// Do not refetch the opacity window attribute unless necessary, this
// is probably an expensive operation in some cases
if (refetch_prop)
w->opacity_prop = get_opacity_prop(dpy, w, OPAQUE);
if (OPAQUE == (opacity = w->opacity_prop)) {
if (OPAQUE != win_type_opacity[w->window_type])
opacity = win_type_opacity[w->window_type] * OPAQUE;
}
// Respect inactive_opacity in some cases
if (IS_NORMAL_WIN(w) && False == w->focused && inactive_opacity
&& (OPAQUE == opacity || inactive_opacity_override))
opacity = inactive_opacity;
set_opacity(dpy, w, opacity);
}
static void
add_win(Display *dpy, Window id, Window prev, Bool override_redirect) {
if (find_win(dpy, id)) {
@ -1632,6 +1665,8 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) {
new->shadow_width = 0;
new->shadow_height = 0;
new->opacity = OPAQUE;
new->opacity_prop = OPAQUE;
new->focused = False;
new->destroyed = False;
new->need_configure = False;
new->window_type = WINTYPE_UNKNOWN;
@ -1661,9 +1696,6 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) {
if (new->a.map_state == IsViewable) {
new->window_type = determine_wintype(dpy, id, id);
if (inactive_opacity && IS_NORMAL_WIN(new)) {
new->opacity = INACTIVE_OPACITY;
}
map_win(dpy, id, new->damage_sequence - 1, True, override_redirect);
}
}
@ -1875,7 +1907,7 @@ destroy_win(Display *dpy, Window id, Bool fade) {
#if HAS_NAME_WINDOW_PIXMAP
if (w && w->pixmap && fade && win_type_fade[w->window_type]) {
set_fade(dpy, w, w->opacity * 1.0 / OPAQUE,
set_fade(dpy, w, get_opacity_percent(dpy, w),
0.0, fade_out_step, destroy_callback,
False, True);
} else
@ -2169,9 +2201,9 @@ ev_focus_in(XFocusChangeEvent *ev) {
if (!inactive_opacity) return;
win *w = find_win(dpy, ev->window);
if (IS_NORMAL_WIN(w)) {
set_opacity(dpy, w, OPAQUE);
}
w->focused = True;
calc_opacity(dpy, w, False);
}
inline static void
@ -2188,9 +2220,9 @@ ev_focus_out(XFocusChangeEvent *ev) {
}
win *w = find_win(dpy, ev->window);
if (IS_NORMAL_WIN(w)) {
set_opacity(dpy, w, INACTIVE_OPACITY);
}
w->focused = False;
calc_opacity(dpy, w, False);
}
inline static void
@ -2282,9 +2314,7 @@ ev_property_notify(XPropertyEvent *ev) {
/* reset mode and redraw window */
win *w = find_win(dpy, ev->window);
if (w) {
double def = win_type_opacity[w->window_type];
set_opacity(dpy, w,
get_opacity_prop(dpy, w, (unsigned long)(OPAQUE * def)));
calc_opacity(dpy, w, True);
}
}
@ -2456,6 +2486,8 @@ usage() {
" Green color value of shadow (0.0 - 1.0, defaults to 0).\n"
"--shadow-blue value\n"
" Blue color value of shadow (0.0 - 1.0, defaults to 0).\n"
"--inactive-opacity-override\n"
" Inactive opacity set by -i overrides value of _NET_WM_OPACITY.\n"
);
exit(1);
@ -2561,6 +2593,7 @@ main(int argc, char **argv) {
{ "shadow-red", required_argument, NULL, 0 },
{ "shadow-green", required_argument, NULL, 0 },
{ "shadow-blue", required_argument, NULL, 0 },
{ "inactive-opacity-override", no_argument, NULL, 0 },
};
XEvent ev;
@ -2603,6 +2636,9 @@ main(int argc, char **argv) {
case 2:
shadow_blue = normalize_d(atof(optarg));
break;
case 3:
inactive_opacity_override = True;
break;
}
break;
// Short options
@ -2664,10 +2700,12 @@ main(int argc, char **argv) {
shadow_offset_y = atoi(optarg);
break;
case 'i':
inactive_opacity = (double)atof(optarg);
inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE);
if (OPAQUE == inactive_opacity)
inactive_opacity = 0;
break;
case 'e':
frame_opacity = (double)atof(optarg);
frame_opacity = normalize_d(atof(optarg));
break;
case 'z':
clear_shadow = True;
@ -2792,6 +2830,37 @@ main(int argc, char **argv) {
add_win(dpy, children[i], i ? children[i-1] : None, False);
}
// Check the currently focused window so we can apply appropriate
// opacity on it
{
Window wid = 0;
int revert_to;
win *w = NULL;
XGetInputFocus(dpy, &wid, &revert_to);
// XGetInputFocus seemingly returns the application window focused
// instead of the WM window frame, so we traverse through its
// ancestors to find out the frame
while(wid && wid != root
&& !array_wid_exists(children, nchildren, wid)) {
Window troot;
Window parent;
Window *tchildren;
unsigned tnchildren;
XQueryTree(dpy, wid, &troot, &parent, &tchildren, &tnchildren);
XFree(tchildren);
wid = parent;
}
// And we set the focus state and opacity here
if (wid && wid != root && (w = find_win(dpy, wid))) {
w->focused = True;
calc_opacity(dpy, w, False);
}
}
XFree(children);
XUngrabServer(dpy);

View File

@ -7,6 +7,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <math.h>
#include <sys/poll.h>
#include <sys/time.h>
@ -44,6 +45,8 @@
* Types
*/
typedef uint32_t opacity_t;
typedef enum {
WINTYPE_UNKNOWN,
WINTYPE_DESKTOP,
@ -94,8 +97,12 @@ typedef struct _win {
int shadow_dy;
int shadow_width;
int shadow_height;
unsigned int opacity;
opacity_t opacity;
/// Cached value of opacity window attribute.
opacity_t opacity_prop;
wintype window_type;
/// Whether the window is focused.
Bool focused;
unsigned long damage_sequence; /* sequence when damage was created */
Bool destroyed;
unsigned int left_width;
@ -127,6 +134,7 @@ typedef struct _fade {
} fade;
extern int root_height, root_width;
/**
* Functions
*/
@ -134,6 +142,11 @@ extern int root_height, root_width;
// inline functions must be made static to compile correctly under clang:
// http://clang.llvm.org/compatibility.html#inline
/**
* Normalize a double value to 0.\ 0 - 1.\ 0.
*
* @param d double value to normalize
*/
static inline double normalize_d(double d) {
if (d > 1.0)
return 1.0;
@ -143,6 +156,23 @@ static inline double normalize_d(double d) {
return d;
}
/**
* Check if a window ID exists in an array of window IDs.
*
* @param arr the array of window IDs
* @param count amount of elements in the array
* @param wid window ID to search for
*/
static inline Bool array_wid_exists(const Window *arr,
int count, Window wid) {
while (count--) {
if (arr[count] == wid)
return True;
}
return False;
}
static int
get_time_in_milliseconds();
@ -263,8 +293,8 @@ unmap_callback(Display *dpy, win *w);
static void
unmap_win(Display *dpy, Window id, Bool fade);
static unsigned int
get_opacity_prop(Display *dpy, win *w, unsigned int def);
opacity_t
get_opacity_prop(Display *dpy, win *w, opacity_t def);
static double
get_opacity_percent(Display *dpy, win *w);
@ -272,8 +302,9 @@ get_opacity_percent(Display *dpy, win *w);
static void
determine_mode(Display *dpy, win *w);
static void
set_opacity(Display *dpy, win *w, unsigned long opacity);
void set_opacity(Display *dpy, win *w, opacity_t opacity);
void calc_opacity(Display *dpy, win *w, Bool refetch_prop);
static void
add_win(Display *dpy, Window id, Window prev, Bool override_redirect);