/** * Beryl Trailfocus - take three * * Copyright (c) 2006 Kristian Lyngstøl * * 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. * * This version is completly rewritten from scratch with opacify as a * basic template. The original trailfocus was written by: * François Ingelrest and rewritten by: * Dennis Kasprzyk * * * Trailfocus modifies the opacity, brightness and saturation on a window * based on when it last had focus. * */ #include #include #include #include #include #include #include #define GET_TRAILFOCUS_DISPLAY(d) \ ((TrailfocusDisplay *) (d)->privates[displayPrivateIndex].ptr) #define TRAILFOCUS_DISPLAY(d) \ TrailfocusDisplay *td = GET_TRAILFOCUS_DISPLAY (d) #define GET_TRAILFOCUS_SCREEN(s, td) \ ((TrailfocusScreen *) (s)->privates[(td)->screenPrivateIndex].ptr) #define TRAILFOCUS_SCREEN(s) \ TrailfocusScreen *ts = GET_TRAILFOCUS_SCREEN (s, GET_TRAILFOCUS_DISPLAY (s->display)) #define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption)) #define N_WIN_TYPE (sizeof (winType) / sizeof (winType[0])) static int displayPrivateIndex = 0; static char *winType[] = { N_("Toolbar"), N_("Utility"), N_("Dialog"), N_("ModalDialog"), N_("Fullscreen"), N_("Normal") }; typedef enum _TfOpt { SOPT_MAX_OPACITY = 0, SOPT_MAX_BRIGHTNESS, SOPT_MAX_SATURATION, SOPT_MIN_OPACITY, SOPT_MIN_BRIGHTNESS, SOPT_MIN_SATURATION, SOPT_WINDOWS_START, SOPT_WINDOWS, SOPT_WINDOW_TYPE, SOPT_IGNORE_SKIPTASKBAR, SOPT_IGNORE_SKIPPAGER, SOPT_NUM } TrailfocusScreenOptions; typedef struct _TrailfocusDisplay { int screenPrivateIndex; HandleEventProc handleEvent; } TrailfocusDisplay; typedef struct _TfWindowAttributes { GLushort opacity; GLushort brightness; GLushort saturation; } TfAttrib; typedef struct _TrailfocusScreen { int wMask; Window *win; int win_max; TfAttrib *inc; CompOption opt[SOPT_NUM]; } TrailfocusScreen; /* Core trailfocus functions. These do the real work. ---------------*/ /* Checks if a window is a window trailfocus is supposed to touch or not. */ static Bool is_trailfocus_window(TrailfocusScreen * ts, CompWindow * w) { if (!(w->type & ts->wMask)) return False; if (w->defaultPaintLock.opacity && w->defaultPaintLock.saturation && w->defaultPaintLock.brightness) return False; if (ts->opt[SOPT_IGNORE_SKIPTASKBAR].value.b && w->state & CompWindowStateSkipTaskbarMask) return False; if (ts->opt[SOPT_IGNORE_SKIPPAGER].value.b && w->state & CompWindowStateSkipPagerMask) return False; return True; } /* The set_[opacity|brightness|saturation] functions of the * respective values. These set the default values, see * paint.c or beryl.h for description of how * default-vs-permanant paint attributes work. */ static inline void set_opacity(CompWindow * w, int opacity) { setDefaultWindowOpacity(w, opacity, PL_NO_LOCK); } static inline void set_brightness(CompWindow * w, int brightness) { setDefaultWindowBrightness(w, brightness, PL_NO_LOCK); } static inline void set_saturation(CompWindow * w, int sat) { setDefaultWindowSaturation(w, sat, PL_NO_LOCK); } /* Resets a window's paint attribute when we're bailing out. * */ static inline void bail_out_window(CompWindow * w) { setWindowBailout(w, PL_NO_LOCK); } /* Meant for unload. Reset all windows handled by us. */ static void bail_out_tf(CompScreen * s) { CompWindow *w; TRAILFOCUS_SCREEN(s); for (w = s->windows; w; w = w->next) if (is_trailfocus_window(ts, w)) bail_out_window(w); } /* Meant for option change. Reset all windows not handled by us. * Ideally we should have a window-diff here, but alas... we don't. * So we reset all we don't handle. Room for improvement if necesarry. */ static void bail_out_non_tf(CompScreen * s) { CompWindow *w; TRAILFOCUS_SCREEN(s); for (w = s->windows; w; w = w->next) if (!is_trailfocus_window(ts, w)) bail_out_window(w); } /* Sets the opacity, saturation and brightness to that of a window * that had focus N times ago. */ static inline void set_window(TrailfocusScreen * ts, CompWindow * w, int n) { set_opacity(w, ts->inc[n].opacity); set_saturation(w, ts->inc[n].saturation); set_brightness(w, ts->inc[n].brightness); } /* Walks through the window-list and sets the opacity-levels for * all windows. The inner loop will result in ts->win[i] either * representing a recently focused window, or the least * focused window. */ static void set_windows(CompScreen * s) { CompWindow *w; TRAILFOCUS_SCREEN(s); int i = 0; for (w = s->windows; w; w = w->next) { if (w->invisible || w->hidden || w->minimized) continue; if (!is_trailfocus_window(ts, w)) continue; for (i = 0; i < ts->win_max; i++) if (w->id == ts->win[i]) break; set_window(ts, w, i); } } /* Push a new window-id on the trailfocus window-stack (not to be * confused with the real window stack). Only keep one copy of a * window on the stack. If the window allready exist on the stack, * move it to the top. */ static CompScreen *push_window(CompDisplay * d, Window id) { int i; short int tmp; CompWindow *w; CompScreen *s; w = findWindowAtDisplay(d, id); if (UNLIKELY(!w)) return NULL; s = w->screen; if (UNLIKELY(!s)) return NULL; TRAILFOCUS_SCREEN(s); if (!is_trailfocus_window(ts, w)) return NULL; tmp = ts->win_max; for (i = 0; i < ts->win_max; i++) if (ts->win[i] == id) break; if (UNLIKELY(i == 0)) return NULL; for (; i > 0; i--) ts->win[i] = ts->win[i - 1]; ts->win[0] = id; return s; } /* Find a window on a screen.... Unlike the findWindowAtScreen which * core provides, we don't intend to search for the same window several * times in a row so we optimize for* the normal situation of searching for * a window only once in a row. */ 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; } /* Walks through the existing stack and removes windows that should * (no longer) be there. Used for option-change. */ static void clean_list(CompScreen * s) { TRAILFOCUS_SCREEN(s); CompWindow *w; int i, j, length; for (i = 0; i < ts->win_max; i++) { w = find_window(s, ts->win[i]); if (!w || !is_trailfocus_window(ts, w)) ts->win[i] = 0; } length = ts->win_max; for (i = 0; i < length; i++) { if (!ts->win[i]) { for (j = i; j < length - 1; j++) ts->win[j] = ts->win[j + 1]; length--; } } for (; length < ts->win_max; length++) ts->win[length] = 0; } /* Handles the event if it was a FocusIn event. */ static void trailfocusHandleEvent(CompDisplay * d, XEvent * event) { TRAILFOCUS_DISPLAY(d); CompScreen *s; switch (event->type) { case FocusIn: s = push_window(d, event->xfocus.window); if (s) set_windows(s); break; default: break; } UNWRAP(td, d, handleEvent); (*d->handleEvent) (d, event); WRAP(td, d, handleEvent, trailfocusHandleEvent); } /* Settings changed. Reallocate rs->inc and re-populate it and the * rest of the TrailfocusScreen (-wMask). */ static void recalculate_attributes(TrailfocusScreen * ts) { TfAttrib tmp, min, max; int i; int start; start = ts->opt[SOPT_WINDOWS_START].value.i - 1; ts->win_max = ts->opt[SOPT_WINDOWS].value.i; if (start >= ts->win_max) { fprintf(stderr, "trailfocus: WARNING: Attempting to define start higher than max windows.\n"); start = ts->win_max - 1; } min.opacity = ts->opt[SOPT_MIN_OPACITY].value.i * OPAQUE / 100; min.brightness = ts->opt[SOPT_MIN_BRIGHTNESS].value.i * OPAQUE / 100; min.saturation = ts->opt[SOPT_MIN_SATURATION].value.i * OPAQUE / 100; max.opacity = ts->opt[SOPT_MAX_OPACITY].value.i * OPAQUE / 100; max.brightness = ts->opt[SOPT_MAX_BRIGHTNESS].value.i * OPAQUE / 100; max.saturation = ts->opt[SOPT_MAX_SATURATION].value.i * OPAQUE / 100; ts->win = realloc(ts->win, sizeof(Window) * (ts->win_max + 1)); ts->inc = realloc(ts->inc, sizeof(TfAttrib) * (ts->win_max + 1)); tmp.opacity = (max.opacity - min.opacity) / ((ts->win_max - start)); tmp.brightness = (max.brightness - min.brightness) / ((ts->win_max - start)); tmp.saturation = (max.saturation - min.saturation) / ((ts->win_max - start)); for (i = 0; i < start; ++i) ts->inc[i] = max; for (i = 0; i + start <= ts->win_max; i++) { ts->inc[i + start].opacity = max.opacity - (tmp.opacity * i); ts->inc[i + start].brightness = max.brightness - (tmp.brightness * i); ts->inc[i + start].saturation = max.saturation - (tmp.saturation * i); ts->win[i + start] = 0; } // ts->inc[i+start] = min; } /* Unlike the traditional setScreenOptions, we return on failure in the * switch, so we can execute a couple of common functions at the end. */ static Bool trailfocusSetScreenOptions(CompScreen * screen, char *name, CompOptionValue * value) { CompOption *o; int index; TRAILFOCUS_SCREEN(screen); o = compFindOption(ts->opt, NUM_OPTIONS(ts), name, &index); if (!o) return FALSE; switch (index) { case SOPT_MAX_OPACITY: case SOPT_MAX_BRIGHTNESS: case SOPT_MAX_SATURATION: case SOPT_MIN_OPACITY: case SOPT_MIN_BRIGHTNESS: case SOPT_MIN_SATURATION: case SOPT_WINDOWS: case SOPT_WINDOWS_START: if (!compSetIntOption(o, value)) return FALSE; recalculate_attributes(ts); break; case SOPT_WINDOW_TYPE: if (!compSetOptionList(o, value)) return FALSE; ts->wMask = compWindowTypeMaskFromStringList(&o->value); break; case SOPT_IGNORE_SKIPPAGER: case SOPT_IGNORE_SKIPTASKBAR: if (!compSetBoolOption(o, value)) return FALSE; break; default: return FALSE; break; } bail_out_non_tf(screen); clean_list(screen); push_window(screen->display, screen->display->activeWindow); set_windows(screen); return TRUE; } /* Configuration, initliazation, boring stuff. ----------------------- */ /* Remember to reset windows to some sane value when we unload */ static void trailfocusFiniScreen(CompPlugin * p, CompScreen * s) { TRAILFOCUS_SCREEN(s); bail_out_tf(s); if (ts->win) free(ts->win); if (ts->inc) free(ts->inc); if (ts->opt[SOPT_WINDOW_TYPE].value.list.value) free(ts->opt[SOPT_WINDOW_TYPE].value.list.value); free(ts); } static void trailfocusScreenInitOptions(TrailfocusScreen * ts) { CompOption *o; int i; o = &ts->opt[SOPT_MAX_OPACITY]; o->name = "max_opacity"; o->group = N_("Appearance"); o->subGroup = N_("Opacity"); o->displayHints = ""; o->shortDesc = N_("Opacity Level of Focused Windows"); o->longDesc = N_("Opacity of the currently Focused Window. Windows " "will get Opacity Levels between the Focused " "and Minimum."); o->type = CompOptionTypeInt; o->value.i = 100; o->rest.i.min = 1; o->rest.i.max = 100; o->advanced = False; o = &ts->opt[SOPT_MAX_BRIGHTNESS]; o->name = "max_brightness"; o->group = N_("Appearance"); o->subGroup = N_("Brightness"); o->displayHints = ""; o->shortDesc = N_("Brightness Level of Focused Windows"); o->longDesc = N_("Brightness of the currently Focused Window. Windows " "will get Brightness Levels between the Focused " "and Minimum. "); o->type = CompOptionTypeInt; o->value.i = 100; o->rest.i.min = 1; o->rest.i.max = 100; o->advanced = False; o = &ts->opt[SOPT_MAX_SATURATION]; o->name = "max_saturation"; o->group = N_("Appearance"); o->subGroup = N_("Saturation"); o->displayHints = ""; o->shortDesc = N_("Saturation Level of Focused Windows"); o->longDesc = N_("Saturation of the currently Focused Window. Windows " "will get Saturation Levels between the Focused " "and Minimum. "); o->type = CompOptionTypeInt; o->value.i = 100; o->rest.i.min = 1; o->rest.i.max = 100; o->advanced = False; o = &ts->opt[SOPT_MIN_OPACITY]; o->name = "min_opacity"; o->group = N_("Appearance"); o->subGroup = N_("Opacity"); o->displayHints = ""; o->shortDesc = N_("Opacity Level of Unfocused Windows"); o->longDesc = N_("Opacity of the least Focused Windows. Windows " "will get Opacity Levels between the Focused " "and Minimum. "); o->type = CompOptionTypeInt; o->value.i = 70; o->rest.i.min = 1; o->rest.i.max = 100; o->advanced = False; o = &ts->opt[SOPT_MIN_BRIGHTNESS]; o->name = "min_brightness"; o->group = N_("Appearance"); o->subGroup = N_("Brightness"); o->displayHints = ""; o->shortDesc = N_("Brightness Level of Unfocused Windows"); o->longDesc = N_("Brightness of the least Focused Windows. Windows " "will get Brightness Levels between the Focused " "and Minimum. "); o->type = CompOptionTypeInt; o->value.i = 100; o->rest.i.min = 1; o->rest.i.max = 100; o->advanced = False; o = &ts->opt[SOPT_MIN_SATURATION]; o->name = "min_saturation"; o->group = N_("Appearance"); o->subGroup = N_("Saturation"); o->displayHints = ""; o->shortDesc = N_("Saturation Level of Unfocused Windows"); o->longDesc = N_("Saturation of the least Focused Windows. Windows " "will get Saturation Levels between the Focused " "and Minimum. "); o->type = CompOptionTypeInt; o->value.i = 100; o->rest.i.min = 1; o->rest.i.max = 100; o->advanced = False; o = &ts->opt[SOPT_WINDOWS]; o->name = "windows"; o->group = N_("Behaviour"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Number of Windows to Track"); o->longDesc = N_("Number of Windows Trailfocus will keep track of. " "Windows had focus this amount of windows ago or " "more, will be considered completly unfocused."); o->type = CompOptionTypeInt; o->value.i = 5; o->rest.i.min = 1; o->rest.i.max = 150; o->advanced = False; ts->win_max = 5; o = &ts->opt[SOPT_WINDOWS_START]; o->name = "windows_start"; o->group = N_("Behaviour"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("What Window to Start Fading"); o->longDesc = N_("This defines when Trailfocus will Start Fading " "Windows, this lets you set up trailfocus to " "treat the N first Windows as fully focused."); o->type = CompOptionTypeInt; o->value.i = 2; o->rest.i.min = 1; o->rest.i.max = 20; o->advanced = False; o = &ts->opt[SOPT_WINDOW_TYPE]; o->name = "window_types"; o->group = N_("Behaviour"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Window Types"); o->longDesc = N_("Window Types that should be handled by Trailfocus."); 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; o = &ts->opt[SOPT_IGNORE_SKIPTASKBAR]; o->advanced = True; o->name = "ignore_skiptaskbar"; o->group = N_("Behaviour"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Ignore \"SkipTaskbar\" Windows"); o->longDesc = N_("Ignore \"SkipTaskbar\" Windows."); o->type = CompOptionTypeBool; o->value.b = True; o = &ts->opt[SOPT_IGNORE_SKIPPAGER]; o->advanced = True; o->name = "ignore_skippager"; o->group = N_("Behaviour"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Ignore \"SkipPager\" Windows"); o->longDesc = N_("Ignore \"SkipPager\" Windows."); o->type = CompOptionTypeBool; o->value.b = True; } /* Remember to populate the TrailFocus screen properly, and push the * active window on the stack, then set windows. */ static Bool trailfocusInitScreen(CompPlugin * p, CompScreen * s) { TRAILFOCUS_DISPLAY(s->display); TrailfocusScreen *ts = (TrailfocusScreen *) calloc(1, sizeof(TrailfocusScreen)); s->privates[td->screenPrivateIndex].ptr = ts; trailfocusScreenInitOptions(ts); ts->wMask = compWindowTypeMaskFromStringList(&ts->opt[SOPT_WINDOW_TYPE]. value); recalculate_attributes(ts); push_window(s->display, s->display->activeWindow); set_windows(s); return TRUE; } static CompOption *trailfocusGetScreenOptions(CompScreen * screen, int *count) { if (screen) { TRAILFOCUS_SCREEN(screen); *count = NUM_OPTIONS(ts); return ts->opt; } else { TrailfocusScreen *ts = malloc(sizeof(TrailfocusScreen)); trailfocusScreenInitOptions(ts); *count = NUM_OPTIONS(ts); return ts->opt; } } static Bool trailfocusInitDisplay(CompPlugin * p, CompDisplay * d) { TrailfocusDisplay *td = (TrailfocusDisplay *) malloc(sizeof(TrailfocusDisplay)); td->screenPrivateIndex = allocateScreenPrivateIndex(d); if (td->screenPrivateIndex < 0) { free(td); return FALSE; } d->privates[displayPrivateIndex].ptr = td; WRAP(td, d, handleEvent, trailfocusHandleEvent); return TRUE; } static void trailfocusFiniDisplay(CompPlugin * p, CompDisplay * d) { TRAILFOCUS_DISPLAY(d); UNWRAP(td, d, handleEvent); freeScreenPrivateIndex(d, td->screenPrivateIndex); free(td); } static Bool trailfocusInit(CompPlugin * p) { displayPrivateIndex = allocateDisplayPrivateIndex(); if (displayPrivateIndex < 0) return FALSE; return TRUE; } static void trailfocusFini(CompPlugin * p) { if (displayPrivateIndex >= 0) freeDisplayPrivateIndex(displayPrivateIndex); } CompPluginVTable trailfocusVTable = { "trailfocus2", N_("Trailfocus"), N_("Adjust the opacity, saturation and brightness of windows based on when they last had focus."), trailfocusInit, trailfocusFini, trailfocusInitDisplay, trailfocusFiniDisplay, trailfocusInitScreen, trailfocusFiniScreen, 0, 0, 0, // trailfocusGetDisplayOptions, 0, // trailfocusSetDisplayOptions, trailfocusGetScreenOptions, trailfocusSetScreenOptions, 0, 0, 0, 0, BERYL_ABI_INFO, "beryl-plugins", "effects", 0, 0, False, }; CompPluginVTable *getCompPluginInfo(void) { return &trailfocusVTable; }