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 * Macros
*/ */
#define INACTIVE_OPACITY \
(unsigned long)((double)inactive_opacity * OPAQUE)
#define IS_NORMAL_WIN(w) \ #define IS_NORMAL_WIN(w) \
((w) && ((w)->window_type == WINTYPE_NORMAL \ ((w) && ((w)->window_type == WINTYPE_NORMAL \
|| (w)->window_type == WINTYPE_UTILITY)) || (w)->window_type == WINTYPE_UTILITY))
@ -108,8 +105,14 @@ Bool fade_trans = False;
Bool clear_shadow = False; Bool clear_shadow = False;
double inactive_opacity = 0; /// Default opacity for inactive windows.
double frame_opacity = 0; /// 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; Bool synchronize = False;
@ -203,21 +206,7 @@ set_fade(Display *dpy, win *w, double start,
} }
f->callback = callback; f->callback = callback;
w->opacity = f->cur * OPAQUE; set_opacity(dpy, w, 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);
}
/* fading windows need to be drawn, mark /* fading windows need to be drawn, mark
them as damaged. when a window maps, them as damaged. when a window maps,
@ -1364,6 +1353,7 @@ map_win(Display *dpy, Window id,
if (!w) return; if (!w) return;
w->focused = False;
w->a.map_state = IsViewable; w->a.map_state = IsViewable;
w->window_type = determine_wintype(dpy, w->id, w->id); 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 calc_opacity(dpy, w, True);
//w->opacity = get_opacity_prop(dpy, w, OPAQUE);
determine_mode(dpy, w); determine_mode(dpy, w);
@ -1481,8 +1470,7 @@ unmap_win(Display *dpy, Window id, Bool fade) {
finish_unmap_win(dpy, w); finish_unmap_win(dpy, w);
} }
static unsigned int opacity_t get_opacity_prop(Display *dpy, win *w, opacity_t def) {
get_opacity_prop(Display *dpy, win *w, unsigned int def) {
Atom actual; Atom actual;
int format; int format;
unsigned long n, left; 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); XA_CARDINAL, &actual, &format, &n, &left, &data);
if (result == Success && data != NULL) { if (result == Success && data != NULL) {
unsigned int i; opacity_t i = *((opacity_t *) data);
memcpy(&i, data, sizeof(unsigned int)); XFree(data);
XFree((void *)data);
return i; return i;
} }
@ -1504,11 +1491,7 @@ get_opacity_prop(Display *dpy, win *w, unsigned int def) {
static double static double
get_opacity_percent(Display *dpy, win *w) { get_opacity_percent(Display *dpy, win *w) {
double def = win_type_opacity[w->window_type]; return w->opacity * 1.0 / OPAQUE;
unsigned int opacity =
get_opacity_prop(dpy, w, (unsigned int)(OPAQUE * def));
return opacity * 1.0 / OPAQUE;
} }
static void static void
@ -1558,8 +1541,11 @@ determine_mode(Display *dpy, win *w) {
} }
} }
static void void set_opacity(Display *dpy, win *w, opacity_t opacity) {
set_opacity(Display *dpy, win *w, unsigned long opacity) { // Do nothing if the opacity does not change
if (w->opacity == opacity)
return;
w->opacity = opacity; w->opacity = opacity;
determine_mode(dpy, w); determine_mode(dpy, w);
if (w->shadow) { 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 static void
add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { add_win(Display *dpy, Window id, Window prev, Bool override_redirect) {
if (find_win(dpy, id)) { 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_width = 0;
new->shadow_height = 0; new->shadow_height = 0;
new->opacity = OPAQUE; new->opacity = OPAQUE;
new->opacity_prop = OPAQUE;
new->focused = False;
new->destroyed = False; new->destroyed = False;
new->need_configure = False; new->need_configure = False;
new->window_type = WINTYPE_UNKNOWN; 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) { if (new->a.map_state == IsViewable) {
new->window_type = determine_wintype(dpy, id, id); 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); 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 HAS_NAME_WINDOW_PIXMAP
if (w && w->pixmap && fade && win_type_fade[w->window_type]) { 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, 0.0, fade_out_step, destroy_callback,
False, True); False, True);
} else } else
@ -2169,9 +2201,9 @@ ev_focus_in(XFocusChangeEvent *ev) {
if (!inactive_opacity) return; if (!inactive_opacity) return;
win *w = find_win(dpy, ev->window); 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 inline static void
@ -2188,9 +2220,9 @@ ev_focus_out(XFocusChangeEvent *ev) {
} }
win *w = find_win(dpy, ev->window); 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 inline static void
@ -2282,9 +2314,7 @@ ev_property_notify(XPropertyEvent *ev) {
/* reset mode and redraw window */ /* reset mode and redraw window */
win *w = find_win(dpy, ev->window); win *w = find_win(dpy, ev->window);
if (w) { if (w) {
double def = win_type_opacity[w->window_type]; calc_opacity(dpy, w, True);
set_opacity(dpy, w,
get_opacity_prop(dpy, w, (unsigned long)(OPAQUE * def)));
} }
} }
@ -2456,6 +2486,8 @@ usage() {
" Green color value of shadow (0.0 - 1.0, defaults to 0).\n" " Green color value of shadow (0.0 - 1.0, defaults to 0).\n"
"--shadow-blue value\n" "--shadow-blue value\n"
" Blue color value of shadow (0.0 - 1.0, defaults to 0).\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); exit(1);
@ -2561,6 +2593,7 @@ main(int argc, char **argv) {
{ "shadow-red", required_argument, NULL, 0 }, { "shadow-red", required_argument, NULL, 0 },
{ "shadow-green", required_argument, NULL, 0 }, { "shadow-green", required_argument, NULL, 0 },
{ "shadow-blue", required_argument, NULL, 0 }, { "shadow-blue", required_argument, NULL, 0 },
{ "inactive-opacity-override", no_argument, NULL, 0 },
}; };
XEvent ev; XEvent ev;
@ -2603,6 +2636,9 @@ main(int argc, char **argv) {
case 2: case 2:
shadow_blue = normalize_d(atof(optarg)); shadow_blue = normalize_d(atof(optarg));
break; break;
case 3:
inactive_opacity_override = True;
break;
} }
break; break;
// Short options // Short options
@ -2664,10 +2700,12 @@ main(int argc, char **argv) {
shadow_offset_y = atoi(optarg); shadow_offset_y = atoi(optarg);
break; break;
case 'i': case 'i':
inactive_opacity = (double)atof(optarg); inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE);
if (OPAQUE == inactive_opacity)
inactive_opacity = 0;
break; break;
case 'e': case 'e':
frame_opacity = (double)atof(optarg); frame_opacity = normalize_d(atof(optarg));
break; break;
case 'z': case 'z':
clear_shadow = True; clear_shadow = True;
@ -2792,6 +2830,37 @@ main(int argc, char **argv) {
add_win(dpy, children[i], i ? children[i-1] : None, False); 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); XFree(children);
XUngrabServer(dpy); XUngrabServer(dpy);

View File

@ -7,6 +7,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <inttypes.h>
#include <math.h> #include <math.h>
#include <sys/poll.h> #include <sys/poll.h>
#include <sys/time.h> #include <sys/time.h>
@ -44,6 +45,8 @@
* Types * Types
*/ */
typedef uint32_t opacity_t;
typedef enum { typedef enum {
WINTYPE_UNKNOWN, WINTYPE_UNKNOWN,
WINTYPE_DESKTOP, WINTYPE_DESKTOP,
@ -94,8 +97,12 @@ typedef struct _win {
int shadow_dy; int shadow_dy;
int shadow_width; int shadow_width;
int shadow_height; int shadow_height;
unsigned int opacity; opacity_t opacity;
/// Cached value of opacity window attribute.
opacity_t opacity_prop;
wintype window_type; wintype window_type;
/// Whether the window is focused.
Bool focused;
unsigned long damage_sequence; /* sequence when damage was created */ unsigned long damage_sequence; /* sequence when damage was created */
Bool destroyed; Bool destroyed;
unsigned int left_width; unsigned int left_width;
@ -127,6 +134,7 @@ typedef struct _fade {
} fade; } fade;
extern int root_height, root_width; extern int root_height, root_width;
/** /**
* Functions * Functions
*/ */
@ -134,6 +142,11 @@ extern int root_height, root_width;
// inline functions must be made static to compile correctly under clang: // inline functions must be made static to compile correctly under clang:
// http://clang.llvm.org/compatibility.html#inline // 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) { static inline double normalize_d(double d) {
if (d > 1.0) if (d > 1.0)
return 1.0; return 1.0;
@ -143,6 +156,23 @@ static inline double normalize_d(double d) {
return 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 static int
get_time_in_milliseconds(); get_time_in_milliseconds();
@ -263,8 +293,8 @@ unmap_callback(Display *dpy, win *w);
static void static void
unmap_win(Display *dpy, Window id, Bool fade); unmap_win(Display *dpy, Window id, Bool fade);
static unsigned int opacity_t
get_opacity_prop(Display *dpy, win *w, unsigned int def); get_opacity_prop(Display *dpy, win *w, opacity_t def);
static double static double
get_opacity_percent(Display *dpy, win *w); get_opacity_percent(Display *dpy, win *w);
@ -272,8 +302,9 @@ get_opacity_percent(Display *dpy, win *w);
static void static void
determine_mode(Display *dpy, win *w); determine_mode(Display *dpy, win *w);
static void void set_opacity(Display *dpy, win *w, opacity_t opacity);
set_opacity(Display *dpy, win *w, unsigned long opacity);
void calc_opacity(Display *dpy, win *w, Bool refetch_prop);
static void static void
add_win(Display *dpy, Window id, Window prev, Bool override_redirect); add_win(Display *dpy, Window id, Window prev, Bool override_redirect);