/**
* Beryl Opacify
*
* Copyright (c) 2006 Kristian Lyngstøl <kristian@beryl-project.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* Opacify increases opacity on targeted windows and reduces it on
* blocking windows, making whatever window you are targeting easily
* visible.
*
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrender.h>
#include <beryl.h>
#define GET_OPACIFY_DISPLAY(d) \
((OpacifyDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define OPACIFY_DISPLAY(d) \
OpacifyDisplay *od = GET_OPACIFY_DISPLAY (d)
#define GET_OPACIFY_SCREEN(s, od) \
((OpacifyScreen *) (s)->privates[(od)->screenPrivateIndex].ptr)
#define OPACIFY_SCREEN(s) \
OpacifyScreen *os = GET_OPACIFY_SCREEN (s, GET_OPACIFY_DISPLAY (s->display))
#define OPACIFY_DISPLAY_OPTION_TOGGLE 0
#define OPACIFY_DISPLAY_OPTION_TOGGLE_FREEZE 1
#define OPACIFY_DISPLAY_OPTION_INIT_TOGGLE 2
#define OPACIFY_DISPLAY_OPTION_NUM 3
#define OPACIFY_TOGGLE_KEY "o"
#define OPACIFY_TOGGLE_MOD CompSuperMask
#define OPACIFY_SCREEN_OPTION_ACTIVE_OP 0
#define OPACIFY_SCREEN_OPTION_PASSIVE_OP 1
#define OPACIFY_SCREEN_OPTION_ONLY_IF_BLOCK 2
#define OPACIFY_SCREEN_OPTION_WINDOW_TYPE 3
#define OPACIFY_SCREEN_OPTION_TIMEOUT 4
#define OPACIFY_SCREEN_OPTION_FOCUS_INSTANT 5
#define OPACIFY_SCREEN_OPTION_NO_DELAY_CHANGE 6
#define OPACIFY_SCREEN_OPTION_NUM 7
#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
#define N_WIN_TYPE (sizeof (winType) / sizeof (winType[0]))
/* Size of the Window array storing passive windows. */
#define MAX_WINDOWS 64
static int displayPrivateIndex = 0;
typedef struct _OpacifyDisplay
{
int screenPrivateIndex;
HandleEventProc handleEvent;
Bool toggle;
Bool toggle_reset;
CompOption opt[OPACIFY_DISPLAY_OPTION_NUM];
} OpacifyDisplay;
static char *winType[] = {
N_("Toolbar"),
N_("Utility"),
N_("Dialog"),
N_("ModalDialog"),
N_("Fullscreen"),
N_("Normal")
};
typedef struct _OpacifyScreen
{
CompWindow *new_active;
Window active;
Window passive[MAX_WINDOWS];
Region intersect;
unsigned short int passive_num;
unsigned int active_op;
unsigned int passive_op;
unsigned int timeout;
int wMask;
Bool only_if_block;
Bool just_moved;
CompTimeoutHandle timeout_handle;
CompOption opt[OPACIFY_SCREEN_OPTION_NUM];
} OpacifyScreen;
/* Core opacify functions. These do the real work. ---------------------*/
/* Finds the corrosponding CompWindow for Window id. Returns NULL if
* there is no such window.
*/
static CompWindow *find_window(CompScreen * s, Window id)
{
CompWindow *w;
for (w = s->windows; w; w = w->next)
if (w->id == id)
return w;
return NULL;
}
/* Sets the real opacity and damages the window if actual opacity and
* requested opacity differs. */
static void set_opacity(CompWindow * w, int opacity)
{
setWindowOpacity(w, opacity, PL_TEMP_HELLO);
}
/* Resets the Window to the original opacity if it still exists.
*/
static void reset_opacity(CompScreen * s, Window ow)
{
CompWindow *w;
if (!ow)
return;
w = find_window(s, ow);
resetWindowOpacity(w, PL_TEMP_HELLO);
}
/* Resets the opacity of windows on the passive list.
*/
static void clear_passive(CompScreen * s)
{
OPACIFY_SCREEN(s);
int i;
for (i = 0; i < os->passive_num; i++)
reset_opacity(s, os->passive[i]);
os->passive_num = 0;
}
/* Dim an (inactive) window. Place it on the passive list and
* update passive_num. Then change the opacity.
*/
static void dim_window(CompScreen * s, CompWindow * w)
{
OPACIFY_SCREEN(s);
if (UNLIKELY(os->passive_num >= MAX_WINDOWS - 1))
{
fprintf(stderr, "opacify: Trying to store information "
"about too many windows, or you hit a bug.\nIf "
"you don't have around %d windows blocking the "
"currently targeted window, please report this.\n",
MAX_WINDOWS);
return;
}
os->passive[os->passive_num++] = w->id;
set_opacity(w, MIN(os->passive_op, w->paint.opacity));
}
/* Walk through all windows, skip until we've passed the active
* window, skip if it's invisible, hidden or minimized, skip if
* it's not a window type we're looking for.
* Dim it if it intersects.
*
* Returns number of changed windows.
*/
static int passive_windows(CompScreen * s, Region a_region)
{
CompWindow *w;
OPACIFY_SCREEN(s);
Bool flag = FALSE;
int i = 0;
for (w = s->windows; w; w = w->next)
{
if (w->id == os->active)
{
flag = TRUE;
continue;
}
if (!flag)
continue;
if (!(w->type & os->wMask))
continue;
if (w->invisible || w->hidden || w->minimized)
continue;
XIntersectRegion(w->region, a_region, os->intersect);
if (!XEmptyRegion(os->intersect))
{
dim_window(s, w);
i++;
}
}
return i;
}
/* Check if we switched active window, reset the old passive windows
* if we did. If we have an active window and switched: reset that too.
* If we have a window (w is true), update the active id and
* passive list. just_moved is to make sure we recalculate opacity after
* moving. We can't reset before moving because if we're using a delay
* and the window being moved is not the active but overlapping, it will
* be reset, which would conflict with move's opacity change.
* FIXME: A more final solution should be to use IPCS to signal
* which window is being moved.
*/
static void opacify_handle_enter(CompScreen * s, CompWindow * w)
{
OPACIFY_SCREEN(s);
int num;
if (screenGrabExist(s, "move", 0))
{
os->just_moved = True;
return;
}
if (screenGrabExist(s, "rotate", "scale", "resize", 0))
{
clear_passive(s);
reset_opacity(s, os->active);
os->active = 0;
return;
}
if (!w || os->active != w->id || os->just_moved)
{
os->just_moved = False;
clear_passive(s);
reset_opacity(s, os->active);
os->active = 0;
}
if (!w)
return;
if (w->id != os->active && (w->type & os->wMask) && !w->shaded)
{
os->active = w->id;
num = passive_windows(s, w->region);
if (num || !os->only_if_block)
set_opacity(w, MAX(os->active_op, w->paint.opacity));
}
}
/* Decides what to do after a timeout occured.
* Either we reset the opacity because we just toggled,
* or we handle the event.
*/
static Bool handle_timeout(void *data)
{
CompScreen *s = (CompScreen *) data;
OPACIFY_SCREEN(s);
OPACIFY_DISPLAY(s->display);
os->timeout_handle = 0;
if (UNLIKELY(!od->toggle))
{
clear_passive(s);
reset_opacity(s, os->active);
os->active = 0;
}
opacify_handle_enter(s, os->new_active);
return FALSE;
}
/* Checks whether we should delay or not.
* Returns true if immediate execution.
*/
static inline Bool check_delay(OpacifyScreen *os, CompScreen *s, CompDisplay *d)
{
if ((os->opt[OPACIFY_SCREEN_OPTION_FOCUS_INSTANT].value.b
&& os->new_active && (os->new_active->id == d->activeWindow)))
return True;
if (!os->timeout)
return True;
if (!os->new_active || (os->new_active->id == s->root))
return False;
if (os->new_active->type & (CompWindowTypeDesktopMask | CompWindowTypeDockMask))
return False;
if (os->opt[OPACIFY_SCREEN_OPTION_NO_DELAY_CHANGE].value.b && os->passive_num)
return True;
return False;
}
/* Takes the inital event.
* If we were configured, recalculate the opacify-windows if
* it was our window.
* If a window was entered: call upon handle_timeout after os->timeout
* micro seconds, or directly if os->timeout is 0 (no delay).
*
* FIXME: In the perfect world, toggle-resetting is done in the action
* handler that does the actual toggling.
*/
static void opacifyHandleEvent(CompDisplay * d, XEvent * event)
{
CompScreen *s;
CompWindow *w = NULL;
OPACIFY_DISPLAY(d);
UNWRAP(od, d, handleEvent);
(*d->handleEvent) (d, event);
WRAP(od, d, handleEvent, opacifyHandleEvent);
if (!od->toggle && !od->toggle_reset)
return;
switch (event->type)
{
case EnterNotify:
s = findScreenAtDisplay(d, event->xcrossing.root);
if (LIKELY(s))
{
OPACIFY_SCREEN(s);
if (!od->toggle && !os->active)
return;
os->new_active = findTopLevelWindowAtScreen(s, event->xcrossing.window);
if (os->timeout_handle)
compRemoveTimeout(os->timeout_handle);
if (check_delay(os,s,d))
handle_timeout(s);
else
os->timeout_handle =
compAddTimeout(os->timeout, handle_timeout, s);
}
break;
case ConfigureNotify:
s = findScreenAtDisplay(d, event->xconfigure.event);
if (LIKELY(s))
{
OPACIFY_SCREEN(s);
if (os->active != event->xconfigure.window)
break;
clear_passive(s);
if (LIKELY(os->active))
w = find_window(s, os->active);
if (w)
passive_windows(s, w->region);
}
break;
default:
break;
}
}
/* Configuration, initialization, boring stuff. ----------------------- */
static void opacifyFiniScreen(CompPlugin * p, CompScreen * s)
{
OPACIFY_SCREEN(s);
OPACIFY_DISPLAY(s->display);
if (os->opt[OPACIFY_SCREEN_OPTION_WINDOW_TYPE].value.list.value)
free(os->opt[OPACIFY_SCREEN_OPTION_WINDOW_TYPE].value.list.value);
removeScreenAction(s, &od->opt[OPACIFY_DISPLAY_OPTION_TOGGLE].value.
action);
if (os->timeout_handle)
compRemoveTimeout(os->timeout_handle);
XDestroyRegion(os->intersect);
free(os);
}
static Bool opacifySetScreenOptions(CompScreen * screen, char *name,
CompOptionValue * value)
{
CompOption *o;
int index;
OPACIFY_SCREEN(screen);
o = compFindOption(os->opt, NUM_OPTIONS(os), name, &index);
if (!o)
return FALSE;
switch (index)
{
case OPACIFY_SCREEN_OPTION_ACTIVE_OP:
if (compSetIntOption(o, value))
{
os->active_op = (o->value.i * 0xffff) / 100;
return TRUE;
}
break;
case OPACIFY_SCREEN_OPTION_PASSIVE_OP:
if (compSetIntOption(o, value))
{
os->passive_op = (o->value.i * 0xffff) / 100;
return TRUE;
}
break;
case OPACIFY_SCREEN_OPTION_TIMEOUT:
if (compSetIntOption(o, value))
{
os->timeout = o->value.i * 100;
return TRUE;
}
break;
case OPACIFY_SCREEN_OPTION_WINDOW_TYPE:
if (compSetOptionList(o, value))
{
os->wMask = compWindowTypeMaskFromStringList(&o->value);
return TRUE;
}
break;
case OPACIFY_SCREEN_OPTION_FOCUS_INSTANT:
case OPACIFY_SCREEN_OPTION_NO_DELAY_CHANGE:
if (compSetBoolOption(o, value))
return TRUE;
break;
case OPACIFY_SCREEN_OPTION_ONLY_IF_BLOCK:
if (compSetBoolOption(o, value))
{
os->only_if_block = o->value.b;
return TRUE;
}
break;
default:
break;
}
return FALSE;
}
static void opacifyScreenInitOptions(OpacifyScreen * os)
{
CompOption *o;
int i;
o = &os->opt[OPACIFY_SCREEN_OPTION_ONLY_IF_BLOCK];
o->name = "only_if_block";
o->group = N_("Misc. options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Only increase opacity if a window is blocking");
o->longDesc =
N_
("Only increase the opacity on the targeted window if it "
"has one or more windows blocking it from view.");
o->type = CompOptionTypeBool;
o->value.b = FALSE;
o->advanced = False;
os->only_if_block = FALSE;
o = &os->opt[OPACIFY_SCREEN_OPTION_FOCUS_INSTANT];
o->name = "focus_instant";
o->group = N_("Misc. options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc =
N_
("Bypass delay when the new active window is the focused window.");
o->longDesc =
N_("Do not wait if the window we are hovering is the focused "
"window. This allows us to instantly see the focused window."
"You probably want to disable this if you are not using "
"\"Click to Focus\".");
o->type = CompOptionTypeBool;
o->value.b = FALSE;
o->advanced = True;
o = &os->opt[OPACIFY_SCREEN_OPTION_NO_DELAY_CHANGE];
o->name = "no_delay_change";
o->group = N_("Misc. options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc =
N_
("Bypass delay when Opacify is reducing the opacity on one or more windows already.");
o->longDesc =
N_("This enables you to let Opacify instantly opacify new "
"windows when you're already making one or more windows invisible. "
"Makes for faster behavior while looking through layers of hidden windows.");
o->type = CompOptionTypeBool;
o->value.b = FALSE;
o->advanced = True;
o = &os->opt[OPACIFY_SCREEN_OPTION_ACTIVE_OP];
o->name = "active_op";
o->group = N_("Misc. options");
o->subGroup = N_("Opacity levels");
o->displayHints = "";
o->shortDesc = N_("Active Opacity");
o->longDesc =
N_
("The minimum opacity to ensure a targeted window has. "
"A target window will have either this opacity or the "
"preset opacity, whichever is higher.");
o->type = CompOptionTypeInt;
o->value.i = 100;
o->rest.i.min = 1;
o->rest.i.max = 100;
o->advanced = False;
os->active_op = OPAQUE;
o = &os->opt[OPACIFY_SCREEN_OPTION_PASSIVE_OP];
o->name = "passive_op";
o->group = N_("Misc. options");
o->subGroup = N_("Opacity levels");
o->displayHints = "";
o->shortDesc = N_("Passive Opacity");
o->longDesc =
N_
("The maximum opacity a window blocking the current targeted "
"window can have. A blocking window will have either this "
"opacity or the preset opacity, whichever is lower.");
o->type = CompOptionTypeInt;
o->value.i = 10;
o->rest.i.min = 1;
o->rest.i.max = 100;
o->advanced = False;
os->passive_op = 0xffff / 10;
o = &os->opt[OPACIFY_SCREEN_OPTION_TIMEOUT];
o->name = "timeout";
o->group = N_("Misc. options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Delay until Opacification");
o->longDesc =
N_
("The delay in 1/10th of a second before Opacify changes "
"opacity after the active window has changed.");
o->type = CompOptionTypeInt;
o->value.i = 7;
o->rest.i.min = 0;
o->rest.i.max = 100;
o->advanced = False;
os->timeout = 700;
o = &os->opt[OPACIFY_SCREEN_OPTION_WINDOW_TYPE];
o->name = "window_types";
o->group = N_("Misc. options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Window Types");
o->longDesc = N_("Window types that should be Opacified.");
o->type = CompOptionTypeList;
o->value.list.type = CompOptionTypeString;
o->value.list.nValue = N_WIN_TYPE;
o->value.list.value = malloc(sizeof(CompOptionValue) * N_WIN_TYPE);
for (i = 0; i < N_WIN_TYPE; i++)
o->value.list.value[i].s = strdup(winType[i]);
o->rest.s.string = (char **)windowTypeString;
o->rest.s.nString = nWindowTypeString;
o->advanced = True;
os->wMask = compWindowTypeMaskFromStringList(&o->value);
}
static Bool opacifyInitScreen(CompPlugin * p, CompScreen * s)
{
OPACIFY_DISPLAY(s->display);
OpacifyScreen *os = (OpacifyScreen *) calloc(1, sizeof(OpacifyScreen));
s->privates[od->screenPrivateIndex].ptr = os;
os->intersect = XCreateRegion();
opacifyScreenInitOptions(os);
os->just_moved = False;
addScreenAction(s, &od->opt[OPACIFY_DISPLAY_OPTION_TOGGLE].value.action);
return TRUE;
}
static CompOption *opacifyGetScreenOptions(CompScreen * screen, int *count)
{
if (screen)
{
OPACIFY_SCREEN(screen);
*count = NUM_OPTIONS(os);
return os->opt;
}
else
{
OpacifyScreen *os = malloc(sizeof(OpacifyScreen));
opacifyScreenInitOptions(os);
*count = NUM_OPTIONS(os);
return os->opt;
}
}
static Bool opacify_toggle(CompDisplay * d, CompAction * ac,
CompActionState state, CompOption * option,
int nOption)
{
OPACIFY_DISPLAY(d);
od->toggle = !od->toggle;
return TRUE;
}
static void opacifyDisplayInitOptions(OpacifyDisplay * od)
{
CompOption *o;
o = &od->opt[OPACIFY_DISPLAY_OPTION_TOGGLE];
o->name = "toggle";
o->group = N_("Bindings");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Toggle Opacify");
o->longDesc =
N_
("Use this to enable/disable Opacify on the fly. Previously "
"opacified windows will not be reset once you disable it like this.");
o->type = CompOptionTypeAction;
o->value.action.initiate = opacify_toggle;
o->value.action.terminate = 0;
o->value.action.bell = FALSE;
o->value.action.edgeMask = 0;
o->value.action.type = CompBindingTypeKey;
o->value.action.state = CompActionStateInitKey;
o->value.action.key.modifiers = OPACIFY_TOGGLE_MOD;
o->value.action.key.keysym = XStringToKeysym(OPACIFY_TOGGLE_KEY);
o->advanced = False;
o = &od->opt[OPACIFY_DISPLAY_OPTION_TOGGLE_FREEZE];
o->name = "toggle_reset";
o->group = N_("Misc. options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Reset opacity to original values when toggling");
o->longDesc =
N_
("Reset the opacity of all windows modified by opacify when "
"toggling Opacify with the defined key-combination.");
o->type = CompOptionTypeBool;
o->value.b = TRUE;
o->advanced = True;
od->toggle_reset = TRUE;
o = &od->opt[OPACIFY_DISPLAY_OPTION_INIT_TOGGLE];
o->name = "init_toggle";
o->group = N_("Misc. options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Toggle opacify on by default");
o->longDesc =
N_
("With this enabled, opacify will be on when you load Opacify, "
"which is usually when you start Beryl.");
o->type = CompOptionTypeBool;
o->value.b = True;
o->advanced = False;
od->toggle = True;
}
static Bool opacifySetDisplayOptions(CompDisplay * display, char *name,
CompOptionValue * value)
{
CompOption *o;
int index;
OPACIFY_DISPLAY(display);
o = compFindOption(od->opt, NUM_OPTIONS(od), name, &index);
if (!o)
return FALSE;
switch (index)
{
case OPACIFY_DISPLAY_OPTION_TOGGLE:
if (setDisplayAction(display, o, value))
return TRUE;
break;
case OPACIFY_DISPLAY_OPTION_INIT_TOGGLE:
if (compSetBoolOption(o, value))
{
od->toggle = o->value.b;
return TRUE;
}
break;
case OPACIFY_DISPLAY_OPTION_TOGGLE_FREEZE:
if (compSetBoolOption(o, value))
{
od->toggle_reset = o->value.b;
return TRUE;
}
break;
default:
break;
}
return FALSE;
}
static CompOption *opacifyGetDisplayOptions(CompDisplay * display, int *count)
{
if (display)
{
OPACIFY_DISPLAY(display);
*count = NUM_OPTIONS(od);
return od->opt;
}
else
{
OpacifyDisplay *od = malloc(sizeof(OpacifyDisplay));
opacifyDisplayInitOptions(od);
*count = NUM_OPTIONS(od);
return od->opt;
}
}
static Bool opacifyInitDisplay(CompPlugin * p, CompDisplay * d)
{
OpacifyDisplay *od = (OpacifyDisplay *) malloc(sizeof(OpacifyDisplay));
od->screenPrivateIndex = allocateScreenPrivateIndex(d);
if (od->screenPrivateIndex < 0)
{
free(od);
return FALSE;
}
opacifyDisplayInitOptions(od);
d->privates[displayPrivateIndex].ptr = od;
WRAP(od, d, handleEvent, opacifyHandleEvent);
return TRUE;
}
static void opacifyFiniDisplay(CompPlugin * p, CompDisplay * d)
{
OPACIFY_DISPLAY(d);
UNWRAP(od, d, handleEvent);
freeScreenPrivateIndex(d, od->screenPrivateIndex);
free(od);
}
static Bool opacifyInit(CompPlugin * p)
{
displayPrivateIndex = allocateDisplayPrivateIndex();
if (displayPrivateIndex < 0)
return FALSE;
return TRUE;
}
static void opacifyFini(CompPlugin * p)
{
if (displayPrivateIndex >= 0)
freeDisplayPrivateIndex(displayPrivateIndex);
}
CompPluginVTable opacifyVTable = {
"opacify",
N_("Opacify"),
N_("Make windows easily visible by hovering the mouse over them"),
opacifyInit,
opacifyFini,
opacifyInitDisplay,
opacifyFiniDisplay,
opacifyInitScreen,
opacifyFiniScreen,
0,
0,
opacifyGetDisplayOptions,
opacifySetDisplayOptions,
opacifyGetScreenOptions,
opacifySetScreenOptions,
0,
0,
0,
0,
BERYL_ABI_INFO,
"beryl-plugins",
"accessibility",
0,
0,
False,
};
CompPluginVTable *getCompPluginInfo(void)
{
return &opacifyVTable;
}
syntax highlighted by Code2HTML, v. 0.9.1