src/application.c | 10 +++ src/application.h | 5 ++ src/superfluous.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/window.c | 1 + 4 files changed, 178 insertions(+), 0 deletions(-) diff --git a/src/application.c b/src/application.c index 5676f25..9514a46 100644 --- a/src/application.c +++ b/src/application.c @@ -448,6 +448,16 @@ wApplicationDestroy(WApplication *wapp) if (wapp->refcount>0) return; +#ifdef BOUNCE_APP + if (wapp->flags.bouncing) + { + /* event.c:handleDestroyNotify forced this destroy + and thereby overlooked the bounce callback */ + wapp->refcount = 1; + return; + } +#endif + scr = wapp->main_window_desc->screen_ptr; main_window = wapp->main_window; diff --git a/src/application.h b/src/application.h index c0165ce..961e1e5 100644 --- a/src/application.h +++ b/src/application.h @@ -46,6 +46,9 @@ typedef struct WApplication { unsigned int skip_next_animation:1; unsigned int hidden:1; unsigned int emulated:1; +#ifdef BOUNCE_APP + unsigned int bouncing:1; +#endif } flags; } WApplication; @@ -58,6 +61,8 @@ WApplication *wApplicationOf(Window window); void wApplicationExtractDirPackIcon(WScreen *scr,char *path, char *wm_instance, char *wm_class); +void wAppBounce(WApplication *); + #ifdef NEWAPPICON #define wApplicationActivate(wapp) do { \ diff --git a/src/superfluous.c b/src/superfluous.c index 8699d81..e9ab2fc 100644 --- a/src/superfluous.c +++ b/src/superfluous.c @@ -41,6 +41,7 @@ #include "window.h" #include "icon.h" #include "appicon.h" +#include "xinerama.h" extern WPreferences wPreferences; @@ -811,3 +812,164 @@ UpdateGhostWindowMove(void *data, int x, int y) #endif /* GHOST_WINDOW_MOVE */ +#ifdef BOUNCE_APP + +#define BOUNCE_HZ 25 +#define BOUNCE_DELAY (1000/BOUNCE_HZ) +#define BOUNCE_HEIGHT 24 +#define BOUNCE_LENGTH 0.3 +#define BOUNCE_DAMP 0.6 + +typedef struct AppBouncerData { + WApplication *wapp; + int count; + int pow; + int dir; + WMHandlerID *timer; +} AppBouncerData; + +static void +doAppBounce(void *arg) +{ + AppBouncerData *data = (AppBouncerData*)arg; + WAppIcon *aicon = data->wapp->app_icon; + +reinit: + if (aicon && data->wapp->refcount > 1) + { + const double ticks = BOUNCE_HZ*BOUNCE_LENGTH; + const double s = sqrt(BOUNCE_HEIGHT)/(ticks/2); + double h = BOUNCE_HEIGHT*pow(BOUNCE_DAMP, data->pow); + double sqrt_h = sqrt(h); + if (h > 3) + { + double offset, x = s * data->count - sqrt_h; + if (x > sqrt_h) + { + ++data->pow; + data->count = 0; + goto reinit; + } else ++data->count; + offset = h - x*x; + + switch(data->dir) + { + case 0: /* left, bounce to right */ + XMoveWindow(dpy, aicon->icon->core->window, + aicon->x_pos + (int)offset, aicon->y_pos); + break; + case 1: /* right, bounce to left */ + XMoveWindow(dpy, aicon->icon->core->window, + aicon->x_pos - (int)offset, aicon->y_pos); + break; + case 2: /* top, bounce down */ + XMoveWindow(dpy, aicon->icon->core->window, + aicon->x_pos, aicon->y_pos + (int)offset); + break; + case 3: /* bottom, bounce up */ + XMoveWindow(dpy, aicon->icon->core->window, + aicon->x_pos, aicon->y_pos - (int)offset); + break; + } + return; + } + XMoveWindow(dpy, aicon->icon->core->window, + aicon->x_pos, aicon->y_pos); + } + + data->wapp->flags.bouncing = 0; + WMDeleteTimerHandler(data->timer); + wApplicationDestroy(data->wapp); + free(data); +} + +static int +bounceDirection(WAppIcon *aicon) +{ + enum { left_e = 1, right_e = 2, top_e = 4, bottom_e = 8 }; + + WScreen *scr = aicon->icon->core->screen_ptr; + WMRect rr, sr; + int l,r,t,b, h,v; + int dir = 0; + + rr.pos.x = aicon->x_pos; + rr.pos.y = aicon->y_pos; + rr.size.width = rr.size.height = 64; + + sr = wGetRectForHead(scr, wGetHeadForRect(scr, rr)); + + l = rr.pos.x - sr.pos.x; + r = sr.pos.x + sr.size.width - rr.pos.x - rr.size.width; + t = rr.pos.y - sr.pos.y; + b = sr.pos.y + sr.size.height - rr.pos.y - rr.size.height; + + if (l < r) { + dir |= left_e; + h = l; + } else { + dir |= right_e; + h = r; + } + + if (t < b) { + dir |= top_e; + v = t; + } else { + dir |= bottom_e; + v = b; + } + + if (h < v) dir &= ~(top_e | bottom_e); + else dir &= ~(left_e | right_e); + + switch(dir) + { + case left_e: + dir = 0; + break; + + case right_e: + dir = 1; + break; + + case top_e: + dir = 2; + break; + + case bottom_e: + dir = 3; + break; + + default: + wwarning(_("impossible direction: %d\n"), dir); + dir = 3; + break; + } + + return dir; +} + +void +wAppBounce(WApplication * wapp) +{ + if (wapp->app_icon && !wapp->flags.bouncing) + { + ++wapp->refcount; + wapp->flags.bouncing = 1; + + AppBouncerData *data = + (AppBouncerData*)malloc(sizeof(AppBouncerData)); + data->wapp = wapp; + data->count = data->pow = 0; + data->dir = bounceDirection(wapp->app_icon); + data->timer = WMAddPersistentTimerHandler(BOUNCE_DELAY, doAppBounce, data); + } +} + +#else +void +wAppBounce(WApplication * wapp) +{ +} +#endif diff --git a/src/window.c b/src/window.c index 4c0a8fc..372a110 100644 --- a/src/window.c +++ b/src/window.c @@ -1399,6 +1399,7 @@ wManageWindow(WScreen *scr, Window window) raise = True; } } + wAppBounce(app); } }