/* * Copyright © 2005 Novell, Inc. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Author: David Reveman */ #include #include #include #include #include #include #include #include #define WIN_X(w) ((w)->attrib.x - (w)->input.left) #define WIN_Y(w) ((w)->attrib.y - (w)->input.top) #define WIN_W(w) ((w)->width + (w)->input.left + (w)->input.right) #define WIN_H(w) ((w)->height + (w)->input.top + (w)->input.bottom) #define SCALE_SPACING_DEFAULT 25 #define SCALE_SPACING_MIN 0 #define SCALE_SPACING_MAX 250 #define SCALE_SLOPPY_FOCUS_DEFAULT TRUE #define SCALE_INITIATE_CURRENT_HEAD_KEY_DEFAULT "F9" #define SCALE_INITIATE_CURRENT_HEAD_MODIFIERS_DEFAULT 0 #define SCALE_INITIATE_KEY_DEFAULT "Pause" #define SCALE_INITIATE_MODIFIERS_DEFAULT CompSuperMask #define SCALE_INITIATE_APP_KEY_DEFAULT "F7" #define SCALE_INITIATE_APP_MODIFIERS_DEFAULT CompSuperMask #define SCALE_INITIATE_ALL_KEY_DEFAULT "F8" #define SCALE_INITIATE_ALL_MODIFIERS_DEFAULT 0 #define SCALE_SPEED_DEFAULT 2.5f #define SCALE_SPEED_MIN 0.1f #define SCALE_SPEED_MAX 50.0f #define SCALE_SPEED_PRECISION 0.1f #define SCALE_HOVER_TIME_DEFAULT 1000 #define SCALE_HOVER_TIME_MIN 50 #define SCALE_HOVER_TIME_MAX 10000 #define SCALE_DARKEN_BACK_FACTOR_DEFAULT 0.67f #define SCALE_DARKEN_BACK_FACTOR_MIN 0.00f #define SCALE_DARKEN_BACK_FACTOR_MAX 1.00f #define SCALE_DARKEN_BACK_FACTOR_PRECISION 0.01f #define SCALE_TIMESTEP_DEFAULT 1.1f #define SCALE_TIMESTEP_MIN 0.1f #define SCALE_TIMESTEP_MAX 50.0f #define SCALE_TIMESTEP_PRECISION 0.1f #define SCALE_STATE_NONE 0 #define SCALE_STATE_OUT 1 #define SCALE_STATE_WAIT 2 #define SCALE_STATE_IN 3 #define SCALE_DARKEN_BACK_DEFAULT TRUE #define SCALE_OPACITY_DEFAULT 75 #define SCALE_OPACITY_MIN 0 #define SCALE_OPACITY_MAX 100 #define SCALE_USE_CLASS_DEFAULT TRUE #define SCALE_HEAD_DEFAULT 0 #define SCALE_HEAD_MIN 0 #define SCALE_HEAD_MAX 10 typedef enum _MultiMonitorMode { AllHeads, Selected, Current } MultiMonitorMode; char *multiMonitorModes[] = { N_("On all monitors"), N_("On selected monitor"), N_("On current monitor") }; #define MULTIMONITOR_MODE_DEFAULT AllHeads #define NUM_MULTIMONITOR_MODES 3 #define SCALE_ORGANIC_DEFAULT FALSE #define SCALE_SHOW_MINIMIZED_DEFAULT FALSE static char *winType[] = { N_("Utility"), N_("Dialog"), N_("ModalDialog"), N_("Fullscreen"), N_("Normal") }; #define N_WIN_TYPE (sizeof (winType) / sizeof (winType[0])) typedef enum { ScaleMethodStandard = 0, ScaleMethodEnhanced, ScaleMethodOrganic } ScaleMethod; static char *scaleMethodString[] = { N_("Standard"), N_("Enhanced"), N_("Organic") }; static ScaleMethod scaleMethod[] = { ScaleMethodStandard, ScaleMethodEnhanced, ScaleMethodOrganic }; #define N_SCALE_METHODS (sizeof (scaleMethodString) / sizeof (scaleMethodString[0])) #define SCALE_METHOD_DEFAULT (scaleMethodString[1]) #define SCALE_METHOD_INT_DEFAULT ScaleMethodEnhanced typedef enum { ScaleIconNone = 0, ScaleIconEmblem, ScaleIconBig } IconOverlay; static char *iconOverlayString[] = { N_("None"), N_("Emblem"), N_("Big") }; static IconOverlay iconOverlay[] = { ScaleIconNone, ScaleIconEmblem, ScaleIconBig }; #define N_ICON_TYPE (sizeof (iconOverlayString) / sizeof (iconOverlayString[0])) #define SCALE_ICON_DEFAULT (iconOverlayString[1]) #define SCALE_ALLOW_KEYBOARD_INPUT_DEFAULT FALSE static int displayPrivateIndex; typedef struct _ScaleSlot { int x1, y1, x2, y2; int line; int filled; float scale; } ScaleSlot; typedef struct _SlotArea { int nWindows; XRectangle workArea; } SlotArea; #define SCALE_DISPLAY_OPTION_INITIATE_NORMAL 0 #define SCALE_DISPLAY_OPTION_INITIATE_APP 1 #define SCALE_DISPLAY_OPTION_INITIATE_ALL 2 #define SCALE_DISPLAY_OPTION_INITIATE_CURRENT_HEAD 3 #define SCALE_DISPLAY_OPTION_NUM 4 typedef struct _ScaleDisplay { int screenPrivateIndex; HandleEventProc handleEvent; CompOption opt[SCALE_DISPLAY_OPTION_NUM]; unsigned int lastActiveNum; KeyCode leftKeyCode, rightKeyCode, upKeyCode, downKeyCode; } ScaleDisplay; #define SCALE_SCREEN_OPTION_SPACING 0 #define SCALE_SCREEN_OPTION_SLOPPY_FOCUS 1 #define SCALE_SCREEN_OPTION_ICON 2 #define SCALE_SCREEN_OPTION_SPEED 3 #define SCALE_SCREEN_OPTION_TIMESTEP 4 #define SCALE_SCREEN_OPTION_WINDOW_TYPE 5 #define SCALE_SCREEN_OPTION_DARKEN_BACK 6 #define SCALE_SCREEN_OPTION_OPACITY 7 #define SCALE_SCREEN_OPTION_USE_CLASS 8 #define SCALE_SCREEN_OPTION_DARKEN_BACK_FACTOR 9 #define SCALE_SCREEN_OPTION_MULTIMONITOR 10 #define SCALE_SCREEN_OPTION_HEAD 11 #define SCALE_SCREEN_OPTION_SCALE_METHOD 12 #define SCALE_SCREEN_OPTION_ALLOW_KEYBOARD_INPUT 13 #define SCALE_SCREEN_OPTION_SHOW_MINIMIZED 14 #define SCALE_SCREEN_OPTION_HOVER_TIME 15 #define SCALE_SCREEN_OPTION_NUM 16 typedef struct _ScaleScreen { int windowPrivateIndex; PreparePaintScreenProc preparePaintScreen; DonePaintScreenProc donePaintScreen; PaintScreenProc paintScreen; PaintWindowProc paintWindow; DamageWindowRectProc damageWindowRect; CompOption opt[SCALE_SCREEN_OPTION_NUM]; int spacing; float speed; float timestep; float darkenBackFactor; unsigned int wMask; Bool grab; int grabIndex; Window dndTarget; CompTimeoutHandle hoverHandle; int state; int moreAdjust; Cursor cursor; ScaleSlot *slots; int slotsSize; int nSlots; int *line; int lineSize; int nLine; /* only used for sorting */ CompWindow **windows; int windowsSize; int nWindows; GLfloat scale; Bool darkenBack; GLushort opacity; Bool currentHead; Bool allScreensMode; Bool onlyCurrent; Bool useClass; CompWindow *currentWindow; CompWindow *hoveredWindow; Window selectedWindow; Window cancelWindow; int head; int currentOutputDev; ScaleMethod scaleMethod; IconOverlay iconOverlay; Bool clicked; MultiMonitorMode mmMode; //helper for interaction with rotate int scaleStateAtom; } ScaleScreen; typedef struct _ScaleWindow { ScaleSlot *slot; int sid; int distance; Bool rescaled; Bool wasMinimized; GLfloat oldScale; int workspace; int origX, origY; GLfloat xVelocity, yVelocity, scaleVelocity; GLfloat scale; GLfloat tx, ty; float delta; Bool adjust; int animationAtom; } ScaleWindow; #define GET_SCALE_DISPLAY(d) \ ((ScaleDisplay *) (d)->privates[displayPrivateIndex].ptr) #define SCALE_DISPLAY(d) \ ScaleDisplay *sd = GET_SCALE_DISPLAY (d) #define GET_SCALE_SCREEN(s, sd) \ ((ScaleScreen *) (s)->privates[(sd)->screenPrivateIndex].ptr) #define SCALE_SCREEN(s) \ ScaleScreen *ss = GET_SCALE_SCREEN (s, GET_SCALE_DISPLAY (s->display)) #define GET_SCALE_WINDOW(w, ss) \ ((ScaleWindow *) (w)->privates[(ss)->windowPrivateIndex].ptr) #define SCALE_WINDOW(w) \ ScaleWindow *sw = GET_SCALE_WINDOW (w, \ GET_SCALE_SCREEN (w->screen, \ GET_SCALE_DISPLAY (w->screen->display))) #define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption)) static Bool scaleSetHead(ScaleScreen * ss, CompScreen * s, int head) { if (s->nOutputDev == 1) { if (head > 0) return FALSE; else ss->head = 0; } else { if (head == -1) ss->head = -1; else if (head > s->nOutputDev) ss->head = -1; else ss->head = head; } return TRUE; } static Bool scaleSetScreenOption(CompScreen * screen, char *name, CompOptionValue * value) { CompOption *o; int index; SCALE_SCREEN(screen); o = compFindOption(ss->opt, NUM_OPTIONS(ss), name, &index); if (!o) return FALSE; switch (index) { case SCALE_SCREEN_OPTION_SPACING: if (compSetIntOption(o, value)) { ss->spacing = o->value.i; return TRUE; } break; case SCALE_SCREEN_OPTION_SLOPPY_FOCUS: if (compSetBoolOption(o, value)) return TRUE; break; case SCALE_SCREEN_OPTION_SCALE_METHOD: if (compSetStringOption(o, value)) { int i; for (i = 0; i < N_SCALE_METHODS; i++) { if (strcmp(o->value.s, scaleMethodString[i]) == 0) { ss->scaleMethod = scaleMethod[i]; return TRUE; } } } break; case SCALE_SCREEN_OPTION_SPEED: if (compSetFloatOption(o, value)) { ss->speed = o->value.f; return TRUE; } break; case SCALE_SCREEN_OPTION_DARKEN_BACK_FACTOR: if (compSetFloatOption(o, value)) { ss->darkenBackFactor = o->value.f; return TRUE; } break; case SCALE_SCREEN_OPTION_TIMESTEP: if (compSetFloatOption(o, value)) { ss->timestep = o->value.f; return TRUE; } break; case SCALE_SCREEN_OPTION_WINDOW_TYPE: if (compSetOptionList(o, value)) { ss->wMask = compWindowTypeMaskFromStringList(&o->value); return TRUE; } break; case SCALE_SCREEN_OPTION_DARKEN_BACK: if (compSetBoolOption(o, value)) { ss->darkenBack = o->value.b; return TRUE; } break; case SCALE_SCREEN_OPTION_USE_CLASS: if (compSetBoolOption(o, value)) { ss->useClass = o->value.b; return TRUE; } case SCALE_SCREEN_OPTION_OPACITY: if (compSetIntOption(o, value)) { ss->opacity = (OPAQUE * o->value.i) / 100; return TRUE; } break; case SCALE_SCREEN_OPTION_ICON: if (compSetStringOption(o, value)) { int i; for (i = 0; i < N_ICON_TYPE; i++) { if (strcmp(o->value.s, iconOverlayString[i]) == 0) { ss->iconOverlay = iconOverlay[i]; return TRUE; } } } break; case SCALE_SCREEN_OPTION_HEAD: if (compSetIntOption(o, value)) { return scaleSetHead(ss, screen, o->value.i); } break; case SCALE_SCREEN_OPTION_HOVER_TIME: if (compSetIntOption(o, value)) return TRUE; break; case SCALE_SCREEN_OPTION_ALLOW_KEYBOARD_INPUT: case SCALE_SCREEN_OPTION_SHOW_MINIMIZED: if (compSetBoolOption(o, value)) return TRUE; break; case SCALE_SCREEN_OPTION_MULTIMONITOR: if (compSetStringOption(o, value)) { int i; MultiMonitorMode mode = MULTIMONITOR_MODE_DEFAULT; for (i = 0; i < o->rest.s.nString; i++) if (strcmp(multiMonitorModes[i], o->value.s) == 0) mode = (MultiMonitorMode) i; ss->mmMode = mode; return TRUE; } break; default: break; } return FALSE; } static void scaleScreenInitOptions(ScaleScreen * ss) { CompOption *o; int i; o = &ss->opt[SCALE_SCREEN_OPTION_SPACING]; o->advanced = False; o->name = "spacing"; o->group = N_("Appearance"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Window Spacing"); o->longDesc = N_("Space between Scaled Windows."); o->type = CompOptionTypeInt; o->value.i = SCALE_SPACING_DEFAULT; o->rest.i.min = SCALE_SPACING_MIN; o->rest.i.max = SCALE_SPACING_MAX; o = &ss->opt[SCALE_SCREEN_OPTION_SLOPPY_FOCUS]; o->advanced = False; o->name = "sloppy_focus"; o->group = N_("Behaviour"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Sloppy Focus"); o->longDesc = N_("Focus follows mouse pointer in scaled mode."); o->type = CompOptionTypeBool; o->value.b = SCALE_SLOPPY_FOCUS_DEFAULT; o = &ss->opt[SCALE_SCREEN_OPTION_SPEED]; o->advanced = False; o->name = "speed"; o->group = N_("Misc. Options"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Scale Speed"); o->longDesc = N_("Scale Speed."); o->type = CompOptionTypeFloat; o->value.f = SCALE_SPEED_DEFAULT; o->rest.f.min = SCALE_SPEED_MIN; o->rest.f.max = SCALE_SPEED_MAX; o->rest.f.precision = SCALE_SPEED_PRECISION; o = &ss->opt[SCALE_SCREEN_OPTION_DARKEN_BACK_FACTOR]; o->advanced = False; o->name = "darken_back_factor"; o->group = N_("Appearance"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Darken Background"); o->longDesc = N_("Set how Dark the Background will become (0.0 to 1.0)."); o->type = CompOptionTypeFloat; o->value.f = SCALE_DARKEN_BACK_FACTOR_DEFAULT; o->rest.f.min = SCALE_DARKEN_BACK_FACTOR_MIN; o->rest.f.max = SCALE_DARKEN_BACK_FACTOR_MAX; o->rest.f.precision = SCALE_DARKEN_BACK_FACTOR_PRECISION; o = &ss->opt[SCALE_SCREEN_OPTION_TIMESTEP]; o->advanced = False; o->name = "timestep"; o->group = N_("Misc. Options"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Scale Timestep"); o->longDesc = N_("Scale Timestep."); o->type = CompOptionTypeFloat; o->value.f = SCALE_TIMESTEP_DEFAULT; o->rest.f.min = SCALE_TIMESTEP_MIN; o->rest.f.max = SCALE_TIMESTEP_MAX; o->rest.f.precision = SCALE_TIMESTEP_PRECISION; o = &ss->opt[SCALE_SCREEN_OPTION_WINDOW_TYPE]; o->advanced = False; 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 Scaled in Scale mode."); 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; ss->wMask = compWindowTypeMaskFromStringList(&o->value); o = &ss->opt[SCALE_SCREEN_OPTION_DARKEN_BACK]; o->advanced = False; o->name = "darken_back"; o->group = N_("Appearance"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Darken Background"); o->longDesc = N_ ("Darken Background when Scaling windows, to make windows stand out more."); o->type = CompOptionTypeBool; o->value.b = SCALE_DARKEN_BACK_DEFAULT; o = &ss->opt[SCALE_SCREEN_OPTION_OPACITY]; o->advanced = False; o->name = "opacity"; o->group = N_("Appearance"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Opacity"); o->longDesc = N_("Amount of Opacity in percent."); o->type = CompOptionTypeInt; o->value.i = SCALE_OPACITY_DEFAULT; o->rest.i.min = SCALE_OPACITY_MIN; o->rest.i.max = SCALE_OPACITY_MAX; o = &ss->opt[SCALE_SCREEN_OPTION_USE_CLASS]; o->advanced = False; o->name = "use_class"; o->group = N_("Misc. Options"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Use WM_CLASS for Only-Current"); o->longDesc = N_ ("Use WM_CLASS to find out whether a window should be Scaled in Only-Current."); o->type = CompOptionTypeBool; o->value.b = SCALE_USE_CLASS_DEFAULT; o = &ss->opt[SCALE_SCREEN_OPTION_MULTIMONITOR]; o->advanced = False; o->name = "multimonitor_mode"; o->group = N_("Behaviour"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("MultiMonitor Mode"); o->longDesc = N_("Multi Monitor Mode behavior."); o->type = CompOptionTypeString; o->value.s = strdup(multiMonitorModes[MULTIMONITOR_MODE_DEFAULT]); o->rest.s.string = multiMonitorModes; o->rest.s.nString = NUM_MULTIMONITOR_MODES; o = &ss->opt[SCALE_SCREEN_OPTION_HEAD]; o->advanced = False; o->name = "head"; o->group = N_("Behaviour"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Head"); o->longDesc = N_("Head on Which windows are Scaled."); o->type = CompOptionTypeInt; o->value.i = SCALE_HEAD_DEFAULT; o->rest.i.min = SCALE_HEAD_MIN; o->rest.i.max = SCALE_HEAD_MAX; o = &ss->opt[SCALE_SCREEN_OPTION_ICON]; o->advanced = False; o->name = "overlay_icon"; o->group = N_("Appearance"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Overlay Icon"); o->longDesc = N_("Overlay an Icon on windows once they are Scaled."); o->type = CompOptionTypeString; o->value.s = strdup(SCALE_ICON_DEFAULT); o->rest.s.string = iconOverlayString; o->rest.s.nString = N_ICON_TYPE; o = &ss->opt[SCALE_SCREEN_OPTION_SCALE_METHOD]; o->advanced = False; o->name = "scale_method"; o->group = N_("Behaviour"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Scaling Method"); o->longDesc = N_("Algorithms for window Placement."); o->type = CompOptionTypeString; o->value.s = strdup(SCALE_METHOD_DEFAULT); o->rest.s.string = scaleMethodString; o->rest.s.nString = N_SCALE_METHODS; o = &ss->opt[SCALE_SCREEN_OPTION_ALLOW_KEYBOARD_INPUT]; o->advanced = False; o->name = "allow_keyboard_input"; o->group = N_("Behaviour"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Allow Keyboard Input"); o->longDesc = N_("Allow Keyboard Input when Scaled."); o->type = CompOptionTypeBool; o->value.b = SCALE_ALLOW_KEYBOARD_INPUT_DEFAULT; o = &ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED]; o->advanced = False; o->name = "show_minimized"; o->group = N_("Behaviour"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Show Minimized Windows"); o->longDesc = N_("Show Minimized Windows."); o->type = CompOptionTypeBool; o->value.b = SCALE_SHOW_MINIMIZED_DEFAULT; o = &ss->opt[SCALE_SCREEN_OPTION_HOVER_TIME]; o->advanced = False; o->name = "hover_time"; o->group = N_("Misc. Options"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Hover Time"); o->longDesc = N_("Time (in ms) before Scale mode is terminated when" "Hovering over a window."); o->type = CompOptionTypeInt; o->value.i = SCALE_HOVER_TIME_DEFAULT; o->rest.i.min = SCALE_HOVER_TIME_MIN; o->rest.i.max = SCALE_HOVER_TIME_MAX; } static CompOption *scaleGetScreenOptions(CompScreen * screen, int *count) { if (screen) { SCALE_SCREEN(screen); *count = NUM_OPTIONS(ss); return ss->opt; } else { ScaleScreen *ss = malloc(sizeof(ScaleScreen)); scaleScreenInitOptions(ss); *count = NUM_OPTIONS(ss); return ss->opt; } } static void gotoViewport(CompScreen * s, int x) { SCALE_SCREEN(s); if (!ss->selectedWindow) return; ss->selectedWindow = 0; XEvent xev; xev.xclient.type = ClientMessage; xev.xclient.display = s->display->display; xev.xclient.format = 32; xev.xclient.message_type = s->display->desktopViewportAtom; xev.xclient.window = s->root; xev.xclient.data.l[0] = x * s->width; xev.xclient.data.l[1] = 0; xev.xclient.data.l[2] = 0; xev.xclient.data.l[3] = 0; xev.xclient.data.l[4] = 0; XSendEvent(s->display->display, s->root, FALSE, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } static void setWinPort(CompWindow * w) { int x, y; SCALE_WINDOW(w); //FIXME - only works with x viewports defaultViewportForWindow(w, &x, &y); sw->workspace = x; } static Bool isNeverScaleWin(CompWindow * w) { if (w->attrib.override_redirect) return TRUE; if (w->state & CompWindowStateOffscreenMask) return TRUE; if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask)) return TRUE; return FALSE; } static Bool isScaleWin(CompWindow * w) { SCALE_SCREEN(w->screen); if (isNeverScaleWin(w)) return FALSE; if (!ss->allScreensMode && ss->currentHead && !ss->onlyCurrent && screenGetOutputDevForWindow(w) != ss->currentOutputDev) { return FALSE; } if (ss->allScreensMode) { if ((!w->mapNum || w->attrib.map_state != IsViewable)) // && ((!w->minimized && !(w->state & CompWindowStateHiddenMask)))) if (!w->minimized || !ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED].value.b) return FALSE; } else { int x, y; defaultViewportForWindow(w, &x, &y); if (x != w->screen->x) return FALSE; if ((!(*w->screen->focusWindow) (w)) && (!w->minimized || !ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED].value.b)) return FALSE; } if (w->state & CompWindowStateShadedMask) return FALSE; if (!w->mapNum || w->attrib.map_state != IsViewable) if (!w->minimized || !ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED].value.b) return FALSE; if (!(ss->wMask & w->type)) return FALSE; if (w->state & CompWindowStateSkipPagerMask) return FALSE; if (ss->onlyCurrent && ss->currentWindow) { if (!ss->useClass && (w->clientLeader != ss->currentWindow->clientLeader)) return FALSE; if (ss->useClass && (strcmp(w->resClass, ss->currentWindow->resClass) != 0)) return FALSE; } if (!(ss->currentWindow)) return TRUE; if (ss->onlyCurrent && ss->currentWindow != w) restackWindowBelow(w, ss->currentWindow); return TRUE; } static Bool isInWorkArea(CompWindow *w, int output) { XRectangle workArea; screenGetOutputDevWorkArea( w->screen, output, &workArea); int wx1 = workArea.x; int wy1 = workArea.y; int wx2 = wx1 + workArea.width; int wy2 = wy1 + workArea.height; int x1 = WIN_X(w); int y1 = WIN_Y(w); int x2 = x1 + WIN_W(w); int y2 = y1 + WIN_H(w); if (x1 >= wx1 && x1 <= wx2) { if (y1 >= wy1 && y1 <= wy2) return TRUE; if (y2 >= wy1 && y2 <= wy2) return TRUE; } if (x2 >= wx1 && x2 <= wx2) { if (y1 >= wy1 && y1 <= wy2) return TRUE; if (y2 >= wy1 && y2 <= wy2) return TRUE; } if (x1 <= wx1 && x2 >= wx1) { if (y1 >= wy1 && y1 <= wy2) return TRUE; if (y2 >= wy1 && y2 <= wy2) return TRUE; if (y1 <= wy1 && y2 >= wy1) return TRUE; } if (y1 <= wy1 && y2 >= wy1) { if (x1 >= wx1 && x1 <= wx2) return TRUE; if (x2 >= wx1 && x2 <= wx2) return TRUE; } return FALSE; } static Bool scalePaintWindow(CompWindow * w, const WindowPaintAttrib * attrib, Region region, unsigned int mask) { CompScreen *s = w->screen; Bool status; SCALE_SCREEN(s); if (ss->grab) { WindowPaintAttrib sAttrib = *attrib; Bool scaled = FALSE; SCALE_WINDOW(w); if (sw->adjust || sw->slot) { if (w->id != ss->selectedWindow && ss->opacity != OPAQUE && ss->state != SCALE_STATE_IN) { // hovered windows will be opaque, even with sloppy focus off if (w == ss->hoveredWindow) sAttrib.opacity = OPAQUE; else sAttrib.opacity = (sAttrib.opacity * ss->opacity) >> 16; } scaled = TRUE; mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK; } else if (ss->state != SCALE_STATE_IN) { /* modify brightness of the other windows */ if (ss->darkenBack) sAttrib.brightness = sAttrib.brightness * ss->darkenBackFactor; /* make non-scaled windows invisible */ if (!(w->type & CompWindowTypeDesktopMask)) { switch (ss->mmMode) { case Current: if (isInWorkArea(w,ss->currentOutputDev)) sAttrib.opacity = 0; break; case Selected: if (isInWorkArea(w,ss->head)) sAttrib.opacity = 0; break; case AllHeads: { Bool inside = FALSE; int i; for (i = 0; i < s->nOutputDev && !inside; i++) inside |= isInWorkArea(w,i); if (inside) sAttrib.opacity = 0; } break; } } } UNWRAP(ss, s, paintWindow); status = (*s->paintWindow) (w, &sAttrib, region, mask); WRAP(ss, s, paintWindow, scalePaintWindow); if (scaled) { sAttrib.xScale = sw->scale; sAttrib.yScale = sw->scale; sAttrib.xTranslate = sw->tx; sAttrib.yTranslate = sw->ty; (*s->drawWindow) (w, &sAttrib, region, mask | PAINT_WINDOW_TRANSFORMED_MASK); } if ((ss->iconOverlay != ScaleIconNone) && scaled) { CompIcon *icon; icon = getWindowIcon(w, 96, 96); if (!icon) icon = getWindowIcon(w, 128, 128); if (!icon) icon = getWindowIcon(w, 256, 256); if (!icon) icon = getWindowIcon(w, 512, 512); if (!icon) icon = w->screen->defaultIcon; if (icon && (icon->texture.name || iconToTexture(w->screen, icon))) { REGION iconReg; CompMatrix matrix; int wx, wy, width, height; int scaledWinWidth, scaledWinHeight; float ds; scaledWinWidth = w->width * sw->scale; scaledWinHeight = w->height * sw->scale; switch (ss->iconOverlay) { case ScaleIconNone: case ScaleIconEmblem: if (icon->width > 96) { sAttrib.xScale = sAttrib.yScale = MIN((48.0f / (float)icon->width), (48.0f / (float)icon->height)); } else sAttrib.xScale = sAttrib.yScale = 1.0f; break; case ScaleIconBig: sAttrib.opacity /= 3; sAttrib.xScale = sAttrib.yScale = MIN(((float)scaledWinWidth / (float)icon->width), ((float)scaledWinHeight / (float)icon->height)); default: break; } width = icon->width; height = icon->height; switch (ss->iconOverlay) { case ScaleIconNone: case ScaleIconEmblem: if (sAttrib.xScale != 1.0f) { wx = w->attrib.x + ((w->width * sw->scale) - (icon->width * sAttrib.xScale)) / sAttrib.xScale; wy = w->attrib.y + ((w->height * sw->scale) - (icon->height * sAttrib.yScale)) / sAttrib.yScale; } else { wx = w->attrib.x + (w->width * sw->scale) - icon->width; wy = w->attrib.y + (w->height * sw->scale) - icon->height; } break; case ScaleIconBig: wx = w->attrib.x + ((scaledWinWidth - width * sAttrib.xScale) / 2) / sAttrib.xScale; wy = w->attrib.y + ((scaledWinHeight - height * sAttrib.yScale) / 2) / sAttrib.yScale; break; default: wx = wy = 0; break; } if (IPCS_GetBool(IPCS_OBJECT(w), sw->animationAtom)) { /*wx-=w->attrib.x; wx/=sw->scale; wx+=w->attrib.x; wy-=w->attrib.y; wy/=sw->scale; wy+=w->attrib.y; */ sAttrib.xScale /= sw->scale; sAttrib.yScale /= sw->scale; } switch (ss->scaleMethod) { case ScaleMethodEnhanced: if (sw->slot) { sw->delta = fabs(sw->slot->x1 - w->serverX) + fabs(sw->slot->y1 - w->serverY) + fabs(1.0f - sw->slot->scale) * 500.0f; } if (sw->delta) { ds = fabs(sw->tx) + fabs(sw->ty) + fabs(1.0f - sw->scale) * 500.0f; if (ds > sw->delta) ds = sw->delta; sAttrib.opacity = (ds * sAttrib.opacity) / sw->delta; } break; default: ds = 1.0f - ss->scale; if (ds) { sAttrib.opacity = (fabs(1.0f - sw->scale) * sAttrib.opacity) / ds; } else if (!sw->slot) { sAttrib.opacity = 0; } } mask |= PAINT_WINDOW_TRANSLUCENT_MASK | PAINT_WINDOW_TRANSFORMED_MASK; iconReg.rects = &iconReg.extents; iconReg.numRects = 1; iconReg.extents.x1 = wx; iconReg.extents.y1 = wy; iconReg.extents.x2 = iconReg.extents.x1 + width; iconReg.extents.y2 = iconReg.extents.y1 + height; matrix = icon->texture.matrix; matrix.x0 -= wx * icon->texture.matrix.xx; matrix.y0 -= wy * icon->texture.matrix.yy; w->vCount = 0; if (iconReg.extents.x1 < iconReg.extents.x2 && iconReg.extents.y1 < iconReg.extents.y2) (*w->screen->addWindowGeometry) (w, &matrix, 1, &iconReg, &iconReg); if (w->vCount) (*w->screen->drawWindowTexture) (w, &icon->texture, &sAttrib, mask); } } } else { UNWRAP(ss, s, paintWindow); status = (*s->paintWindow) (w, attrib, region, mask); WRAP(ss, s, paintWindow, scalePaintWindow); } return status; } static int compareWindows(const void *elem1, const void *elem2) { CompWindow *w1 = *((CompWindow **) elem1); CompWindow *w2 = *((CompWindow **) elem2); if (!(WIN_X(w1) - WIN_X(w2))) return WIN_Y(w1) - WIN_Y(w2); return WIN_X(w1) - WIN_X(w2); } /** * experimental organic layout method * inspired by smallwindows (smallwindows.sf.net) by Jens Egeblad * */ #define ORGANIC_STEP 0.05 static int organicCompareWindows(const void *elem1, const void *elem2) { CompWindow *w1 = *((CompWindow **) elem1); CompWindow *w2 = *((CompWindow **) elem2); return (WIN_X(w1) + WIN_Y(w1)) - (WIN_X(w2) + WIN_Y(w2)); } static double layoutOrganicCalculateOverlap(CompScreen * s, int win, int x, int y) { SCALE_SCREEN(s); int i; int x1 = x; int y1 = y; int x2 = x1 + WIN_W(ss->windows[win]) * ss->scale; int y2 = y1 + WIN_H(ss->windows[win]) * ss->scale; int overlapx = 0, overlapy = 0; int xmin, xmax; int ymin, ymax; double result = -0.01; for (i = 0; i < ss->nWindows; i++) { if (i == win) continue; overlapx = overlapy = 0; xmax = MAX(ss->slots[i].x1, x1); xmin = MIN(ss->slots[i].x1 + WIN_W(ss->windows[i]) * ss->scale, x2); if (xmax <= xmin) overlapx = xmin - xmax; ymax = MAX(ss->slots[i].y1, y1); ymin = MIN(ss->slots[i].y1 + WIN_H(ss->windows[i]) * ss->scale, y2); if (ymax <= ymin) overlapy = ymin - ymax; result += (double)overlapx *overlapy; } return result; } static double layoutOrganicFindBestHorizontalPosition(CompScreen * s, int win, int *bestx, int areaWidth) { SCALE_SCREEN(s); int i; int y1 = ss->slots[win].y1; int y2 = ss->slots[win].y1 + WIN_H(ss->windows[win]) * ss->scale; double bestoverlap = 1e31, overlap; int w = WIN_W(ss->windows[win]) * ss->scale; *bestx = ss->slots[win].x1; for (i = 0; i < ss->nWindows; i++) { if (i == win) continue; if (ss->slots[i].y1 < y2 && ss->slots[i].y1 + WIN_H(ss->windows[i]) * ss->scale > y1) { if (ss->slots[i].x1 - w >= 0) { double overlap = layoutOrganicCalculateOverlap(s, win, ss->slots[i]. x1 - w, y1); if (overlap < bestoverlap) { *bestx = ss->slots[i].x1 - w; bestoverlap = overlap; } } if (WIN_W(ss->windows[i]) * ss->scale + ss->slots[i].x1 + w < areaWidth) { double overlap = layoutOrganicCalculateOverlap(s, win, ss->slots[i]. x1 + WIN_W(ss-> windows [i]) * ss->scale, y1); if (overlap < bestoverlap) { *bestx = ss->slots[i].x1 + WIN_W(ss->windows[i]) * ss->scale; bestoverlap = overlap; } } } } overlap = layoutOrganicCalculateOverlap(s, win, 0, y1); if (overlap < bestoverlap) { *bestx = 0; bestoverlap = overlap; } overlap = layoutOrganicCalculateOverlap(s, win, areaWidth - w, y1); if (overlap < bestoverlap) { *bestx = areaWidth - w; bestoverlap = overlap; } return bestoverlap; } static double layoutOrganicFindBestVerticalPosition(CompScreen * s, int win, int *besty, int areaHeight) { SCALE_SCREEN(s); int i; int x1 = ss->slots[win].x1; int x2 = ss->slots[win].x1 + WIN_W(ss->windows[win]) * ss->scale; double bestoverlap = 1e31, overlap; int h = WIN_H(ss->windows[win]) * ss->scale; *besty = ss->slots[win].y1; for (i = 0; i < ss->nWindows; i++) { if (i == win) continue; if (ss->slots[i].x1 < x2 && ss->slots[i].x1 + WIN_W(ss->windows[i]) * ss->scale > x1) { if (ss->slots[i].y1 - h >= 0 && ss->slots[i].y1 < areaHeight) { double overlap = layoutOrganicCalculateOverlap(s, win, x1, ss->slots[i]. y1 - h); if (overlap < bestoverlap) { *besty = ss->slots[i].y1 - h; bestoverlap = overlap; } } if (WIN_H(ss->windows[i]) * ss->scale + ss->slots[i].y1 > 0 && WIN_H(ss->windows[i]) * ss->scale + h + ss->slots[i].y1 < areaHeight) { double overlap = layoutOrganicCalculateOverlap(s, win, x1, WIN_H(ss-> windows [i]) * ss->scale + ss->slots[i]. y1); if (overlap < bestoverlap) { *besty = ss->slots[i].y1 + WIN_H(ss->windows[i]) * ss->scale; bestoverlap = overlap; } } } } overlap = layoutOrganicCalculateOverlap(s, win, x1, 0); if (overlap < bestoverlap) { *besty = 0; bestoverlap = overlap; } overlap = layoutOrganicCalculateOverlap(s, win, x1, areaHeight - h); if (overlap < bestoverlap) { *besty = areaHeight - h; bestoverlap = overlap; } return bestoverlap; } static Bool layoutOrganicLocalSearch(CompScreen * s, int areaWidth, int areaHeight) { SCALE_SCREEN(s); Bool improvement = FALSE; int i; double totaloverlap; do { improvement = FALSE; for (i = 0; i < ss->nWindows; i++) { Bool improved = FALSE; do { int newx, newy; double oldoverlap, overlaph, overlapv; improved = FALSE; oldoverlap = layoutOrganicCalculateOverlap(s, i, ss->slots[i].x1, ss->slots[i].y1); overlaph = layoutOrganicFindBestHorizontalPosition(s, i, &newx, areaWidth); overlapv = layoutOrganicFindBestVerticalPosition(s, i, &newy, areaHeight); if (overlaph < oldoverlap - 0.1 || overlapv < oldoverlap - 0.1) { improved = TRUE; improvement = TRUE; if (overlapv > overlaph) ss->slots[i].x1 = newx; else ss->slots[i].y1 = newy; } } while (improved); } } while (improvement); totaloverlap = 0.0; for (i = 0; i < ss->nWindows; i++) { totaloverlap += layoutOrganicCalculateOverlap(s, i, ss->slots[i].x1, ss->slots[i].y1); } return (totaloverlap > 0.1); } static void layoutOrganicRemoveOverlap(CompScreen * s, int areaWidth, int areaHeight) { int i; SCALE_SCREEN(s); CompWindow *w; while (layoutOrganicLocalSearch(s, areaWidth, areaHeight)) { for (i = 0; i < ss->nWindows; i++) { int centerX, centerY; int newX, newY, newWidth, newHeight; w = ss->windows[i]; centerX = ss->slots[i].x1 + WIN_W(w) / 2; centerY = ss->slots[i].y1 + WIN_H(w) / 2; newWidth = (int)((1.0 - ORGANIC_STEP) * (double)WIN_W(w)) - ss->spacing / 2; newHeight = (int)((1.0 - ORGANIC_STEP) * (double)WIN_H(w)) - ss->spacing / 2; newX = centerX - (newWidth / 2); newY = centerY - (newHeight / 2); ss->slots[i].x1 = newX; ss->slots[i].y1 = newY; ss->slots[i].x2 = newX + WIN_W(w); ss->slots[i].y2 = newY + WIN_H(w); } ss->scale -= ORGANIC_STEP; } } /* TODO: Place window thumbnails at smarter positions */ static Bool layoutStandardThumbs(CompScreen * s) { CompWindow *w; int i, j, y2; int cx, cy; int head; int lineLength, itemsPerLine; float scaleW, scaleH; int totalWidth, totalHeight; XRectangle outputRect, workArea; SCALE_SCREEN(s); if (ss->mmMode == Current) { head = screenGetCurrentOutputDev(s); screenGetOutputDevRect(s, head, &outputRect); screenGetOutputDevWorkArea(s, head, &workArea); } else if (ss->mmMode == Selected) { head = ss->head; screenGetOutputDevRect(s, head, &outputRect); screenGetOutputDevWorkArea(s, head, &workArea); } else { outputRect.x = 0; outputRect.y = 0; outputRect.width = s->width; outputRect.height = s->height; workArea = s->workArea; } cx = cy = ss->nWindows = 0; for (w = s->windows; w; w = w->next) { SCALE_WINDOW(w); if (sw->slot) sw->adjust = TRUE; sw->slot = 0; if (!isScaleWin(w)) continue; if (ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED].value.b && w->minimized) { sw->wasMinimized = TRUE; unminimizeWindow(w); } if (!ss->grab) setWinPort(w); if (ss->windowsSize <= ss->nWindows) { ss->windows = realloc(ss->windows, sizeof(CompWindow *) * (ss->nWindows + 32)); if (!ss->windows) return FALSE; ss->windowsSize = ss->nWindows + 32; } ss->windows[ss->nWindows++] = w; } if (ss->nWindows == 0) return FALSE; qsort(ss->windows, ss->nWindows, sizeof(CompWindow *), compareWindows); itemsPerLine = (sqrt(ss->nWindows) * outputRect.width) / outputRect.height; if (itemsPerLine < 1) itemsPerLine = 1; if (ss->lineSize <= ss->nWindows / itemsPerLine + 1) { ss->line = realloc(ss->line, sizeof(int) * (ss->nWindows / itemsPerLine + 2)); if (!ss->line) return FALSE; ss->lineSize = ss->nWindows / itemsPerLine + 2; } totalWidth = totalHeight = 0; ss->line[0] = 0; ss->nLine = 1; lineLength = itemsPerLine; if (ss->slotsSize <= ss->nWindows) { ss->slots = realloc(ss->slots, sizeof(ScaleSlot) * (ss->nWindows + 1)); if (!ss->slots) return FALSE; ss->slotsSize = ss->nWindows + 1; } ss->nSlots = 0; for (i = 0; i < ss->nWindows; i++) { SCALE_WINDOW(ss->windows[i]); w = ss->windows[i]; if (ss->onlyCurrent) raiseWindow(w); /* find a good place between other elements */ for (j = 0; j < ss->nSlots; j++) { y2 = ss->slots[j].y2 + ss->spacing + WIN_H(w); if (w->width < ss->slots[j].x2 - ss->slots[j].x1 && y2 <= ss->line[ss->slots[j].line]) break; } /* otherwise append or start a new line */ if (j == ss->nSlots) { if (lineLength < itemsPerLine) { lineLength++; ss->slots[ss->nSlots].x1 = cx; ss->slots[ss->nSlots].y1 = cy; ss->slots[ss->nSlots].x2 = cx + WIN_W(w); ss->slots[ss->nSlots].y2 = cy + WIN_H(w); ss->slots[ss->nSlots].line = ss->nLine - 1; ss->line[ss->nLine - 1] = MAX(ss->line[ss->nLine - 1], ss->slots[ss->nSlots].y2); } else { lineLength = 1; cx = ss->spacing; cy = ss->line[ss->nLine - 1] + ss->spacing; ss->slots[ss->nSlots].x1 = cx; ss->slots[ss->nSlots].y1 = cy; ss->slots[ss->nSlots].x2 = cx + WIN_W(w); ss->slots[ss->nSlots].y2 = cy + WIN_H(w); ss->slots[ss->nSlots].line = ss->nLine - 1; ss->line[ss->nLine] = ss->slots[ss->nSlots].y2; ss->nLine++; } if (ss->slots[ss->nSlots].y2 > totalHeight) totalHeight = ss->slots[ss->nSlots].y2; } else { ss->slots[ss->nSlots].x1 = ss->slots[j].x1; ss->slots[ss->nSlots].y1 = ss->slots[j].y2 + ss->spacing; ss->slots[ss->nSlots].x2 = ss->slots[ss->nSlots].x1 + WIN_W(w); ss->slots[ss->nSlots].y2 = ss->slots[ss->nSlots].y1 + WIN_H(w); ss->slots[ss->nSlots].line = ss->slots[j].line; ss->slots[j].line = 0; } cx = ss->slots[ss->nSlots].x2; if (cx > totalWidth) totalWidth = cx; cx += ss->spacing; sw->slot = &ss->slots[ss->nSlots]; sw->adjust = TRUE; ss->nSlots++; } totalWidth += ss->spacing; totalHeight += ss->spacing; scaleW = (float)workArea.width / totalWidth; scaleH = (float)workArea.height / totalHeight; ss->scale = MIN(MIN(scaleH, scaleW), 1.0f); for (i = 0; i < ss->nWindows; i++) { SCALE_WINDOW(ss->windows[i]); if (sw->slot) { ss->slots[i].y1 += ss->windows[i]->input.top; ss->slots[i].x1 += ss->windows[i]->input.left; ss->slots[i].y2 += ss->windows[i]->input.top; ss->slots[i].x2 += ss->windows[i]->input.left; ss->slots[i].y1 = (float)ss->slots[i].y1 * ss->scale; ss->slots[i].x1 = (float)ss->slots[i].x1 * ss->scale; ss->slots[i].y2 = (float)ss->slots[i].y2 * ss->scale; ss->slots[i].x2 = (float)ss->slots[i].x2 * ss->scale; ss->slots[i].x1 += workArea.x; ss->slots[i].y1 += workArea.y; ss->slots[i].x2 += workArea.x; ss->slots[i].y2 += workArea.y; } } return TRUE; } static int compareWindowsDistance(const void *elem1, const void *elem2) { CompWindow *w1 = *((CompWindow **) elem1); CompWindow *w2 = *((CompWindow **) elem2); SCALE_SCREEN(w1->screen); return GET_SCALE_WINDOW(w1, ss)->distance - GET_SCALE_WINDOW(w2, ss)->distance; } static void layoutSlotsForArea(CompScreen * s, XRectangle workArea, int numWin) { int i, j, x, y, width, height, lines, n, nS; lines = sqrt(numWin + 1); SCALE_SCREEN(s); if (!numWin) return; nS = 0; y = workArea.y + ss->spacing; height = (workArea.height - (lines + 1) * ss->spacing) / lines; for (i = 0; i < lines; i++) { n = MIN(numWin - nS, ceilf((float)numWin / lines)); x = workArea.x + ss->spacing; width = (workArea.width - (n + 1) * ss->spacing) / n; for (j = 0; j < n; j++) { ss->slots[ss->nSlots].x1 = x; ss->slots[ss->nSlots].y1 = y; ss->slots[ss->nSlots].x2 = x + width; ss->slots[ss->nSlots].y2 = y + height; ss->slots[ss->nSlots].filled = FALSE; x += width + ss->spacing; ss->nSlots++; nS++; } y += height + ss->spacing; } } static SlotArea* getSlotAreas(CompScreen *s) { SCALE_SCREEN(s); int i; XRectangle workArea; float *size = malloc(s->nOutputDev * sizeof(int)); SlotArea* sA = malloc(s->nOutputDev * sizeof(SlotArea)); float sum = 0; int left = ss->nWindows; for (i = 0; i < s->nOutputDev; i++) { // determinate the size of the workarea for each output device screenGetOutputDevWorkArea(s, i, &workArea); size[i] = workArea.width * workArea.height; sum += size[i]; sA[i].nWindows = 0; sA[i].workArea = workArea; } // calculate size available for each window float spw = sum / ss->nWindows; for (i = 0; i < s->nOutputDev && left; i++) { // fill the areas with windows int nw = floor(size[i] / spw); nw = MIN(nw,left); size[i] -= nw * spw; sA[i].nWindows = nw; left -= nw; } // add left windows to output devices with the biggest free space while (left > 0) { int num = 0; float big = 0; for (i = 0; i < s->nOutputDev; i++) if (size[i] > big) { num = i; big = size[i]; } size[num] -= spw; sA[num].nWindows++; left--; } free(size); return sA; } static void layoutSlots(CompScreen * s) { int head = 0, i; XRectangle workArea; SCALE_SCREEN(s); ss->nSlots = 0; if (ss->mmMode == Current || s->nOutputDev == 1) head = screenGetCurrentOutputDev(s); else if (ss->mmMode == Selected) head = ss->head; if (ss->mmMode == Current || ss->mmMode == Selected || s->nOutputDev == 1) { screenGetOutputDevWorkArea(s, head, &workArea); layoutSlotsForArea(s, workArea, ss->nWindows); } else if (ss->mmMode == AllHeads) { SlotArea* sa = getSlotAreas(s); if (sa) { for (i = 0; i < s->nOutputDev; i++) layoutSlotsForArea(s, sa[i].workArea, sa[i].nWindows); free(sa); } } } static void findBestSlots(CompScreen * s) { CompWindow *w; int i, j, d, d0 = 0; float sx, sy, cx, cy; SCALE_SCREEN(s); for (i = 0; i < ss->nWindows; i++) { w = ss->windows[i]; SCALE_WINDOW(w); if (sw->slot) continue; sw->sid = 0; sw->distance = MAXSHORT; for (j = 0; j < ss->nSlots; j++) { if (!ss->slots[j].filled) { sx = (ss->slots[j].x2 + ss->slots[j].x1) / 2; sy = (ss->slots[j].y2 + ss->slots[j].y1) / 2; cx = w->serverX + w->width / 2; cy = w->serverY + w->height / 2; cx -= sx; cy -= sy; d = sqrt(cx * cx + cy * cy); if (d0 + d < sw->distance) { sw->sid = j; sw->distance = d0 + d; } } } d0 += sw->distance; } } static Bool fillInWindows(CompScreen * s) { CompWindow *w; int i, width, height; float sx, sy, cx, cy; SCALE_SCREEN(s); for (i = 0; i < ss->nWindows; i++) { w = ss->windows[i]; SCALE_WINDOW(w); if (!sw->slot) { if (ss->slots[sw->sid].filled) return TRUE; sw->slot = &ss->slots[sw->sid]; width = w->width + w->input.left + w->input.right; height = w->height + w->input.top + w->input.bottom; sx = (float)(sw->slot->x2 - sw->slot->x1) / width; sy = (float)(sw->slot->y2 - sw->slot->y1) / height; sw->slot->scale = MIN(MIN(sx, sy), 1.0f); sx = w->width * sw->slot->scale; sy = w->height * sw->slot->scale; cx = (sw->slot->x1 + sw->slot->x2) / 2; cy = (sw->slot->y1 + sw->slot->y2) / 2; cx += (w->input.left - w->input.right) * sw->slot->scale; cy += (w->input.top - w->input.bottom) * sw->slot->scale; sw->slot->x1 = cx - sx / 2; sw->slot->y1 = cy - sy / 2; sw->slot->x2 = cx + sx / 2; sw->slot->y2 = cy + sy / 2; sw->slot->filled = TRUE; sw->adjust = TRUE; } } return FALSE; } static Bool layoutEnhancedThumbs(CompScreen * s) { CompWindow *w; int i; SCALE_SCREEN(s); ss->nWindows = 0; /* add windows scale list, top most window first */ for (w = s->reverseWindows; w; w = w->prev) { SCALE_WINDOW(w); if (sw->slot) sw->adjust = TRUE; sw->slot = 0; if (!isScaleWin(w)) continue; if (ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED].value.b && w->minimized) { sw->wasMinimized = TRUE; unminimizeWindow(w); } if (!ss->grab) setWinPort(w); if (ss->windowsSize <= ss->nWindows) { ss->windows = realloc(ss->windows, sizeof(CompWindow *) * (ss->nWindows + 32)); if (!ss->windows) return FALSE; ss->windowsSize = ss->nWindows + 32; } ss->windows[ss->nWindows++] = w; } if (ss->nWindows == 0) return FALSE; if (ss->slotsSize < ss->nWindows) { ss->slots = realloc(ss->slots, sizeof(ScaleSlot) * ss->nWindows); if (!ss->slots) return FALSE; ss->slotsSize = ss->nWindows; } /* create a grid of slots */ layoutSlots(s); do { /* find most appropriate slots for windows */ findBestSlots(s); /* sort windows, window with closest distance to a slot first */ qsort(ss->windows, ss->nWindows, sizeof(CompWindow *), compareWindowsDistance); } while (fillInWindows(s)); for (i = 0; i < ss->nWindows; i++) { SCALE_WINDOW(ss->windows[i]); if (ss->onlyCurrent) raiseWindow(ss->windows[i]); if (sw->slot) sw->adjust = TRUE; } return TRUE; } static Bool layoutOrganicThumbs(CompScreen * s) { CompWindow *w; int i; int cx, cy; int head; XRectangle workArea; SCALE_SCREEN(s); if (ss->mmMode == Current) { head = screenGetCurrentOutputDev(s); screenGetOutputDevWorkArea(s, head, &workArea); } else if (ss->mmMode == Selected) { head = ss->head; screenGetOutputDevWorkArea(s, head, &workArea); } else { workArea = s->workArea; } ss->scale = 1.0f; cx = cy = ss->nWindows = 0; for (w = s->windows; w; w = w->next) { SCALE_WINDOW(w); if (sw->slot) sw->adjust = TRUE; sw->slot = 0; if (!isScaleWin(w)) continue; if (ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED].value.b && w->minimized) { sw->wasMinimized = TRUE; unminimizeWindow(w); } if (!ss->grab) setWinPort(w); if (ss->windowsSize <= ss->nWindows) { ss->windows = realloc(ss->windows, sizeof(CompWindow *) * (ss->nWindows + 32)); if (!ss->windows) return FALSE; ss->windowsSize = ss->nWindows + 32; } ss->windows[ss->nWindows++] = w; } if (ss->nWindows == 0) return FALSE; qsort(ss->windows, ss->nWindows, sizeof(CompWindow *), organicCompareWindows); if (ss->slotsSize <= ss->nWindows) { ss->slots = realloc(ss->slots, sizeof(ScaleSlot) * (ss->nWindows + 1)); if (!ss->slots) return FALSE; ss->slotsSize = ss->nWindows + 1; } for (i = 0; i < ss->nWindows; i++) { SCALE_WINDOW(ss->windows[i]); w = ss->windows[i]; sw->slot = &ss->slots[i]; ss->slots[i].x1 = WIN_X(w) - workArea.x; ss->slots[i].y1 = WIN_Y(w) - workArea.y; ss->slots[i].x2 = WIN_X(w) + WIN_W(w) - workArea.x; ss->slots[i].y2 = WIN_Y(w) + WIN_H(w) - workArea.y; if (ss->slots[i].x1 < 0) { ss->slots[i].x2 += abs(ss->slots[i].x1); ss->slots[i].x1 = 0; } if (ss->slots[i].x2 > workArea.width - workArea.x) { ss->slots[i].x1 -= abs(ss->slots[i].x2 - workArea.width); ss->slots[i].x2 = workArea.width - workArea.x; } if (ss->slots[i].y1 < 0) { ss->slots[i].y2 += abs(ss->slots[i].y1); ss->slots[i].y1 = 0; } if (ss->slots[i].y2 > workArea.height - workArea.y) { ss->slots[i].y1 -= abs(ss->slots[i].y2 - workArea.height - workArea.y); ss->slots[i].y2 = workArea.height - workArea.y; } } ss->nSlots = ss->nWindows; layoutOrganicRemoveOverlap(s, workArea.width - workArea.x, workArea.height - workArea.y); for (i = 0; i < ss->nWindows; i++) { SCALE_WINDOW(ss->windows[i]); if (ss->onlyCurrent) raiseWindow(ss->windows[i]); ss->slots[i].x1 += ss->windows[i]->input.left + workArea.x; ss->slots[i].x2 += ss->windows[i]->input.left + workArea.x; ss->slots[i].y1 += ss->windows[i]->input.top + workArea.y; ss->slots[i].y2 += ss->windows[i]->input.top + workArea.y; sw->adjust = TRUE; } return TRUE; } static void zoomWindow(CompScreen * s, CompWindow * w) { SCALE_WINDOW(w); XRectangle workArea, outputRect; int head; head = screenGetCurrentOutputDev(s); screenGetOutputDevRect(s, head, &outputRect); screenGetOutputDevWorkArea(s, screenGetCurrentOutputDev(s), &workArea); if (sw->rescaled == FALSE) { raiseWindow(w); //backup old values sw->oldScale = sw->scale; sw->origX = sw->slot->x1; sw->origY = sw->slot->y1; sw->scale = 1.0f; sw->adjust = TRUE; sw->rescaled = TRUE; sw->slot->x1 = (outputRect.width / 2) - (WIN_W(w) / 2) + workArea.x; sw->slot->y1 = (outputRect.height / 2) - (WIN_H(w) / 2) + workArea.y; } else { restackWindowAbove(w, s->windows); // TODO make sure this is right sw->rescaled = FALSE; sw->scale = sw->oldScale; sw->slot->x1 = sw->origX; sw->slot->y1 = sw->origY; sw->adjust = TRUE; } } static void resetZoomValues(CompScreen * s) { CompWindow *w; for (w = s->windows; w; w = w->next) { SCALE_WINDOW(w); if (sw->rescaled && isScaleWin(w)) sw->rescaled = FALSE; } } /* TODO: Place window thumbnails at smarter positions */ static Bool layoutThumbs(CompScreen * s) { SCALE_SCREEN(s); switch (ss->scaleMethod) { case ScaleMethodStandard: return layoutStandardThumbs(s); case ScaleMethodEnhanced: return layoutEnhancedThumbs(s); case ScaleMethodOrganic: return layoutOrganicThumbs(s); } return FALSE; } static int adjustScaleVelocity(CompWindow * w) { float dx, dy, ds, adjust, amount; float x1, y1, scale; SCALE_SCREEN(w->screen); SCALE_WINDOW(w); if (sw->slot) { x1 = sw->slot->x1; y1 = sw->slot->y1; switch (ss->scaleMethod) { case ScaleMethodEnhanced: scale = sw->slot->scale; break; default: scale = ss->scale; } } else { x1 = w->serverX; y1 = w->serverY; scale = 1.0f; } dx = x1 - (w->serverX + sw->tx); adjust = dx * 0.15f; amount = fabs(dx) * 1.5f; if (amount < 0.5f) amount = 0.5f; else if (amount > 5.0f) amount = 5.0f; sw->xVelocity = (amount * sw->xVelocity + adjust) / (amount + 1.0f); dy = y1 - (w->serverY + sw->ty); adjust = dy * 0.15f; amount = fabs(dy) * 1.5f; if (amount < 0.5f) amount = 0.5f; else if (amount > 5.0f) amount = 5.0f; sw->yVelocity = (amount * sw->yVelocity + adjust) / (amount + 1.0f); ds = scale - sw->scale; adjust = ds * 0.1f; amount = fabs(ds) * 7.0f; if (amount < 0.01f) amount = 0.01f; else if (amount > 0.15f) amount = 0.15f; sw->scaleVelocity = (amount * sw->scaleVelocity + adjust) / (amount + 1.0f); if (fabs(dx) < 0.1f && fabs(sw->xVelocity) < 0.2f && fabs(dy) < 0.1f && fabs(sw->yVelocity) < 0.2f && fabs(ds) < 0.001f && fabs(sw->scaleVelocity) < 0.002f) { sw->xVelocity = sw->yVelocity = sw->scaleVelocity = 0.0f; sw->tx = x1 - w->serverX; sw->ty = y1 - w->serverY; if (sw->rescaled == FALSE) { sw->scale = scale; } return 0; } return 1; } static Bool scalePaintScreen(CompScreen * s, const ScreenPaintAttrib * sAttrib, Region region, int output, unsigned int mask) { Bool status; SCALE_SCREEN(s); if (ss->grab) mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK; UNWRAP(ss, s, paintScreen); status = (*s->paintScreen) (s, sAttrib, region, output, mask); WRAP(ss, s, paintScreen, scalePaintScreen); return status; } static void scalePreparePaintScreen(CompScreen * s, int msSinceLastPaint) { SCALE_SCREEN(s); ss->clicked = FALSE; if (ss->grab && !IPCS_GetBoolND(IPCS_OBJECT(s), "SHOWDESKTOP_ACTIVE", FALSE)) { CompWindow *w; int steps; float amount, chunk; amount = msSinceLastPaint * 0.05f * ss->speed; steps = amount / (0.5f * ss->timestep); if (!steps) steps = 1; chunk = amount / (float)steps; while (steps--) { ss->moreAdjust = 0; for (w = s->windows; w; w = w->next) { SCALE_WINDOW(w); if (sw->adjust) { sw->adjust = adjustScaleVelocity(w); ss->moreAdjust |= sw->adjust; sw->tx += sw->xVelocity * chunk; sw->ty += sw->yVelocity * chunk; if (sw->rescaled == FALSE) { sw->scale += sw->scaleVelocity * chunk; } } } if (!ss->moreAdjust) break; } } UNWRAP(ss, s, preparePaintScreen); (*s->preparePaintScreen) (s, msSinceLastPaint); WRAP(ss, s, preparePaintScreen, scalePreparePaintScreen); } static void scaleDonePaintScreen(CompScreen * s) { SCALE_SCREEN(s); if (ss->grab) { if (ss->moreAdjust) { damageScreen(s); } else { if (ss->state == SCALE_STATE_IN) { ss->grab = FALSE; removeScreenGrabKeyboardOptional(s, ss->grabIndex, 0, !ss-> opt [SCALE_SCREEN_OPTION_ALLOW_KEYBOARD_INPUT]. value.b); ss->grabIndex = 0; if (ss->selectedWindow) { CompWindow *w = findWindowAtScreen(s, ss->selectedWindow); if (w && isScaleWin(w)) { SCALE_WINDOW(w); raiseWindow(w); gotoViewport(s, sw->workspace); } } } else if (ss->state == SCALE_STATE_OUT) ss->state = SCALE_STATE_WAIT; } } UNWRAP(ss, s, donePaintScreen); (*s->donePaintScreen) (s); WRAP(ss, s, donePaintScreen, scaleDonePaintScreen); } static CompWindow *scaleCheckForWindowAt(CompScreen * s, int x, int y) { int x1, y1, x2, y2; CompWindow *w; for (w = s->reverseWindows; w; w = w->prev) { SCALE_WINDOW(w); if (sw->slot) { x1 = w->attrib.x - w->input.left * sw->scale; y1 = w->attrib.y - w->input.top * sw->scale; x2 = w->attrib.x + (w->width + w->input.right) * sw->scale; y2 = w->attrib.y + (w->height + w->input.bottom) * sw->scale; x1 += sw->tx; y1 += sw->ty; x2 += sw->tx; y2 += sw->ty; if (x1 <= x && y1 <= y && x2 > x && y2 > y) return w; } } return 0; } static void sendDndStatusMessage(CompScreen * s, Window source) { XEvent xev; SCALE_SCREEN(s); xev.xclient.type = ClientMessage; xev.xclient.display = s->display->display; xev.xclient.format = 32; xev.xclient.message_type = s->display->xdndStatusAtom; xev.xclient.window = source; xev.xclient.data.l[0] = ss->dndTarget; xev.xclient.data.l[1] = 2; xev.xclient.data.l[2] = 0; xev.xclient.data.l[3] = 0; xev.xclient.data.l[4] = None; XSendEvent(s->display->display, source, FALSE, 0, &xev); } static Bool scaleTerminate(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { CompScreen *s; Window xid; SCALE_DISPLAY(d); xid = getIntOptionNamed(option, nOption, "root", 0); for (s = d->screens; s; s = s->next) { SCALE_SCREEN(s); if (xid && s->root != xid) continue; ss->currentWindow = 0; ss->allScreensMode = FALSE; ss->onlyCurrent = FALSE; //clean up any sw->rescaled values that may still be true //causes odd behavior on next scale/zoom otherwise resetZoomValues(s); if (ss->grab) { if (ss->dndTarget) { XUnmapWindow(d->display, ss->dndTarget); ss->dndTarget = 0; } if (ss->state == SCALE_STATE_NONE) { if (ss->grabIndex) { removeScreenGrabKeyboardOptional(s, ss->grabIndex, 0, !ss-> opt [SCALE_SCREEN_OPTION_ALLOW_KEYBOARD_INPUT]. value.b); ss->grabIndex = 0; } ss->grab = FALSE; if (ss->selectedWindow && !(state & CompActionStateCancel)) { CompWindow *w = findWindowAtScreen(s, ss->selectedWindow); if (w) { SCALE_WINDOW(w); raiseWindow(w); gotoViewport(s, sw->workspace); } } } else { CompWindow *w; for (w = s->windows; w; w = w->next) { SCALE_WINDOW(w); if (sw->slot) { sw->slot = 0; sw->adjust = TRUE; } if (sw->wasMinimized && (w->id != ss->selectedWindow)) { minimizeWindow(w); sw->wasMinimized = FALSE; } if (w->id == ss->selectedWindow) { raiseWindow(w); //goto viewport immediately gotoViewport(s, sw->workspace); } } ss->state = SCALE_STATE_IN; damageScreen(s); } if (state & CompActionStateCancel) { CompWindow *aw = findTopLevelWindowAtScreen(s, ss->cancelWindow); if (aw) sendWindowActivationRequest(s, aw->id); ss->cancelWindow = 0; ss->selectedWindow = 0; } sd->lastActiveNum = None; } } return FALSE; } static Bool scaleEnsureDndRedirectWindow(CompScreen * s) { SCALE_SCREEN(s); if (!ss->dndTarget) { XSetWindowAttributes attr; long xdndVersion = 3; attr.override_redirect = TRUE; ss->dndTarget = XCreateWindow(s->display->display, s->root, 0, 0, 1, 1, 0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, &attr); XChangeProperty(s->display->display, ss->dndTarget, s->display->xdndAwareAtom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdndVersion, 1); } XMoveResizeWindow(s->display->display, ss->dndTarget, 0, 0, s->width, s->height); XMapRaised(s->display->display, ss->dndTarget); return TRUE; } #define SCALE_KIND_NORMAL 0 #define SCALE_KIND_APP 1 #define SCALE_KIND_ALL 2 #define SCALE_KIND_CURRENT_HEAD 3 static Bool scaleInitiateReal(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption, int scaleKind) { CompScreen *s; CompWindow *w; Window xid; Window xid2; xid = getIntOptionNamed(option, nOption, "root", 0); s = findScreenAtDisplay(d, xid); if (s) { SCALE_SCREEN(s); if (ss->clicked) { ss->clicked = FALSE; return FALSE; } w = findTopLevelWindowAtScreen(s, s->display->activeWindow); xid2 = w ? w->id : 0; SCALE_DISPLAY(s->display); if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT) { //ensure we are facing the proper viewport gotoViewport(s, s->x); ss->cancelWindow = ss->selectedWindow = 0; if (s->display->activeWindow) { CompWindow *aw = findTopLevelWindowAtScreen(s, s->display-> activeWindow); //make sure that the selected window is a valid one if (aw && isScaleWin(aw)) { ss->cancelWindow = aw->id; ss->selectedWindow = aw->id; } } ss->onlyCurrent = (scaleKind == SCALE_KIND_APP); if (ss->onlyCurrent) { ss->currentWindow = findWindowAtDisplay(d, xid2); } ss->allScreensMode = (scaleKind == SCALE_KIND_ALL); ss->currentHead = (scaleKind == SCALE_KIND_CURRENT_HEAD); ss->currentOutputDev = screenGetCurrentOutputDev(s); if (!layoutThumbs(s)) return FALSE; if (otherScreenGrabExist(s, "scale", 0)) return FALSE; if (state & CompActionStateInitEdgeDnd) { if (scaleEnsureDndRedirectWindow(s)) ss->grab = TRUE; } else if (!ss->grabIndex) { ss->grabIndex = pushScreenGrabKeyboardOptional(s, ss->cursor, "scale", !ss-> opt [SCALE_SCREEN_OPTION_ALLOW_KEYBOARD_INPUT]. value.b); if (ss->grabIndex) ss->grab = TRUE; } if (ss->grab) { if (!sd->lastActiveNum) sd->lastActiveNum = s->activeNum - 1; ss->state = SCALE_STATE_OUT; damageScreen(s); } } else { state |= CompActionStateCancel; scaleTerminate(d, action, state, option, nOption); } } return FALSE; } static Bool scaleInitiate(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { return scaleInitiateReal(d, action, state, option, nOption, SCALE_KIND_NORMAL); } static Bool scaleInitiateApp(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { return scaleInitiateReal(d, action, state, option, nOption, SCALE_KIND_APP); } static Bool scaleInitiateAll(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { return scaleInitiateReal(d, action, state, option, nOption, SCALE_KIND_ALL); } static Bool scaleInitiateCurrentHead(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { return scaleInitiateReal(d, action, state, option, nOption, SCALE_KIND_CURRENT_HEAD); } static void scaleSelectWindow(CompWindow * w) { SCALE_SCREEN(w->screen); //raiseWindow (w); moveInputFocusToWindow(w); ss->selectedWindow = w->id; damageScreen(w->screen); } static Bool scaleHoverWindowAt(CompScreen * s, int x, int y, Bool sloppyFocus) { CompWindow *w; SCALE_SCREEN(s); w = scaleCheckForWindowAt(s, x, y); if (w && isScaleWin(w)) { if (sloppyFocus) scaleSelectWindow(w); else { ss->hoveredWindow = w; damageScreen(w->screen); } return TRUE; } else ss->hoveredWindow = NULL; return FALSE; } static Bool scaleSelectWindowAt(CompScreen * s, int x, int y) { CompWindow *w; w = scaleCheckForWindowAt(s, x, y); if (w && isScaleWin(w)) { scaleSelectWindow(w); return TRUE; } return FALSE; } static void scaleMoveFocusWindow(CompScreen * s, int dx, int dy) { CompWindow *active; active = findWindowAtScreen(s, s->display->activeWindow); if (active) { CompWindow *w, *focus = NULL; ScaleSlot *slot; int x, y, cx, cy, d, min = MAXSHORT; SCALE_SCREEN(s); SCALE_WINDOW(active); if (!sw->slot) return; cx = (sw->slot->x1 + sw->slot->x2) / 2; cy = (sw->slot->y1 + sw->slot->y2) / 2; for (w = s->windows; w; w = w->next) { if (w->id == active->id) continue; slot = GET_SCALE_WINDOW(w, ss)->slot; if (!slot) continue; x = (slot->x1 + slot->x2) / 2; y = (slot->y1 + slot->y2) / 2; d = sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)); if (d < min) { if ((dx > 0 && slot->x1 < sw->slot->x2) || (dx < 0 && slot->x2 > sw->slot->x1) || (dy > 0 && slot->y1 < sw->slot->y2) || (dy < 0 && slot->y2 > sw->slot->y1)) /* if ((dx > 0 && x < cx) || (dx < 0 && x > cx) || (dy > 0 && y < cy) || (dy < 0 && y > cy)) */ continue; min = d; focus = w; } } if (focus) { SCALE_DISPLAY(s->display); sd->lastActiveNum = focus->activeNum; scaleSelectWindow(focus); } } } static void scaleWindowRemove(CompDisplay * d, Window id) { CompWindow *w; w = findWindowAtDisplay(d, id); if (w) { SCALE_SCREEN(w->screen); if (ss->grab && ss->state != SCALE_STATE_IN) { int i; for (i = 0; i < ss->nWindows; i++) { if (ss->windows[i] == w) { if (layoutThumbs(w->screen)) { ss->state = SCALE_STATE_OUT; damageScreen(w->screen); break; } } } } } } static Bool scaleHoverTimeout(void *closure) { CompScreen *s = closure; SCALE_SCREEN(s); SCALE_DISPLAY(s->display); if (ss->grab && ss->state != SCALE_STATE_IN) { CompOption o; CompAction *action = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_NORMAL].value.action; o.type = CompOptionTypeInt; o.name = "root"; o.value.i = s->root; scaleTerminate(s->display, action, 0, &o, 1); } ss->hoverHandle = 0; return FALSE; } static void scaleHandleEvent(CompDisplay * d, XEvent * event) { CompScreen *s; SCALE_DISPLAY(d); switch (event->type) { case KeyPress: s = findScreenAtDisplay(d, event->xkey.root); if (s) { SCALE_SCREEN(s); if (ss->grab) { if (event->xkey.keycode == sd->leftKeyCode) scaleMoveFocusWindow(s, -1, 0); else if (event->xkey.keycode == sd->rightKeyCode) scaleMoveFocusWindow(s, 1, 0); else if (event->xkey.keycode == sd->upKeyCode) scaleMoveFocusWindow(s, 0, -1); else if (event->xkey.keycode == sd->downKeyCode) scaleMoveFocusWindow(s, 0, 1); } } break; case ButtonPress: s = findScreenAtDisplay(d, event->xbutton.root); if (s) { CompAction *action = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_NORMAL].value. action; SCALE_SCREEN(s); if (ss->grab && ss->state != SCALE_STATE_IN) { ss->clicked = TRUE; CompOption o; o.type = CompOptionTypeInt; o.name = "root"; o.value.i = s->root; if (event->xbutton.button == Button3) { CompWindow *w; w = scaleCheckForWindowAt(s, event->xbutton.x_root, event->xbutton.y_root); if (w) zoomWindow(s, w); } else if (event->xbutton.button == Button2) { CompWindow *w; w = scaleCheckForWindowAt(s, event->xbutton.x_root, event->xbutton.y_root); if (w) closeWindow(w, event->xkey.time); } else if (scaleSelectWindowAt(s, event->xbutton.x_root, event->xbutton.y_root) && event->xbutton.button == Button1) { scaleTerminate(d, action, 0, &o, 1); } else if (event->xbutton.button == Button1) { scaleTerminate(d, action, 0, &o, 1); } else ss->clicked = FALSE; /*else if (event->xbutton.x_root > s->workArea.x && event->xbutton.x_root < (s->workArea.x + s->workArea.width) && event->xbutton.y_root > s->workArea.y && event->xbutton.y_root < (s->workArea.y + s->workArea.height)) { scaleTerminate (d, action, 0, &o, 1); } */ //disabled exit-scale-by-invalid-click-into-show-desktop } } break; case MotionNotify: s = findScreenAtDisplay(d, event->xmotion.root); if (s) { SCALE_SCREEN(s); if (ss->grab && ss->state != SCALE_STATE_IN) scaleHoverWindowAt(s, event->xmotion.x_root, event->xmotion.y_root, ss->opt[SCALE_SCREEN_OPTION_SLOPPY_FOCUS]. value.b); } break; case ClientMessage: if (event->xclient.message_type == d->xdndPositionAtom) { CompWindow *w; w = findWindowAtDisplay(d, event->xclient.window); if (w) { SCALE_SCREEN(w->screen); s = w->screen; if (w->id == ss->dndTarget) sendDndStatusMessage(w->screen, event->xclient.data.l[0]); if (ss->grab && ss->state != SCALE_STATE_IN && w->id == ss->dndTarget) { int x = event->xclient.data.l[2] >> 16; int y = event->xclient.data.l[2] & 0xffff; w = scaleCheckForWindowAt(s, x, y); if (w && isScaleWin(w)) { if (ss->hoverHandle) { if (w->activeNum != sd->lastActiveNum) { compRemoveTimeout(ss->hoverHandle); ss->hoverHandle = 0; } } if (!ss->hoverHandle) ss->hoverHandle = compAddTimeout(ss-> opt [SCALE_SCREEN_OPTION_HOVER_TIME]. value.i, scaleHoverTimeout, s); scaleSelectWindowAt(s, x, y); } else { if (ss->hoverHandle) compRemoveTimeout(ss->hoverHandle); ss->hoverHandle = 0; } } } } else if (event->xclient.message_type == d->xdndDropAtom || event->xclient.message_type == d->xdndLeaveAtom) { CompWindow *w; w = findWindowAtDisplay(d, event->xclient.window); if (w) { CompAction *action = &sd-> opt[SCALE_DISPLAY_OPTION_INITIATE_NORMAL].value. action; SCALE_SCREEN(w->screen); if (ss->grab && ss->state != SCALE_STATE_IN && w->id == ss->dndTarget) { CompOption o; o.type = CompOptionTypeInt; o.name = "root"; o.value.i = w->screen->root; scaleTerminate(d, action, 0, &o, 1); } } } break; default: break; } UNWRAP(sd, d, handleEvent); (*d->handleEvent) (d, event); WRAP(sd, d, handleEvent, scaleHandleEvent); switch (event->type) { case UnmapNotify: scaleWindowRemove(d, event->xunmap.window); break; case DestroyNotify: scaleWindowRemove(d, event->xdestroywindow.window); break; } } static Bool scaleDamageWindowRect(CompWindow * w, Bool initial, BoxPtr rect) { Bool status = FALSE; SCALE_SCREEN(w->screen); if (initial) { if (ss->grab && isScaleWin(w)) { if (layoutThumbs(w->screen)) { ss->state = SCALE_STATE_OUT; damageScreen(w->screen); } } } else if (ss->state == SCALE_STATE_WAIT) { SCALE_WINDOW(w); if (sw->slot) { damageTransformedWindowRect(w, sw->scale, sw->scale, sw->tx, sw->ty, rect); status = TRUE; } } UNWRAP(ss, w->screen, damageWindowRect); status |= (*w->screen->damageWindowRect) (w, initial, rect); WRAP(ss, w->screen, damageWindowRect, scaleDamageWindowRect); return status; } static Bool scaleSetDisplayOption(CompDisplay * display, char *name, CompOptionValue * value) { CompOption *o; int index; SCALE_DISPLAY(display); o = compFindOption(sd->opt, NUM_OPTIONS(sd), name, &index); if (!o) return FALSE; switch (index) { case SCALE_DISPLAY_OPTION_INITIATE_NORMAL: case SCALE_DISPLAY_OPTION_INITIATE_APP: case SCALE_DISPLAY_OPTION_INITIATE_CURRENT_HEAD: case SCALE_DISPLAY_OPTION_INITIATE_ALL: if (setDisplayAction(display, o, value)) return TRUE; default: break; } return FALSE; } static void scaleDisplayInitOptions(ScaleDisplay * sd) { CompOption *o; o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_NORMAL]; o->advanced = False; o->name = "initiate"; o->group = N_("Bindings"); o->subGroup = N_("Initiate Window Picker"); o->displayHints = ""; o->shortDesc = N_("Initiate Window Picker"); o->longDesc = N_("Layout and start transforming Windows."); o->type = CompOptionTypeAction; o->value.action.initiate = scaleInitiate; o->value.action.terminate = scaleTerminate; o->value.action.bell = FALSE; o->value.action.edgeMask = 0; o->value.action.state = CompActionStateInitEdge;; o->value.action.state |= CompActionStateInitEdgeDnd; o->value.action.type = CompBindingTypeKey; o->value.action.state |= CompActionStateInitKey; o->value.action.state |= CompActionStateInitButton; o->value.action.key.modifiers = SCALE_INITIATE_MODIFIERS_DEFAULT; o->value.action.key.keysym = XStringToKeysym(SCALE_INITIATE_KEY_DEFAULT); o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_APP]; o->advanced = False; o->name = "initiate_app"; o->group = N_("Bindings"); o->subGroup = N_("Initiate Window Picker for Current App"); o->displayHints = ""; o->shortDesc = N_("Initiate Window Picker for Current App"); o->longDesc = N_ ("Layout and start transforming Windows of Current Application."); o->type = CompOptionTypeAction; o->value.action.initiate = scaleInitiateApp; o->value.action.terminate = scaleTerminate; o->value.action.bell = FALSE; o->value.action.edgeMask = 0; o->value.action.state = CompActionStateInitEdge; o->value.action.state |= CompActionStateInitEdgeDnd; o->value.action.type = CompBindingTypeKey; o->value.action.state |= CompActionStateInitKey; o->value.action.state |= CompActionStateInitButton; o->value.action.key.modifiers = SCALE_INITIATE_APP_MODIFIERS_DEFAULT; o->value.action.key.keysym = XStringToKeysym(SCALE_INITIATE_APP_KEY_DEFAULT); o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_ALL]; o->advanced = False; o->name = "initiate_all"; o->group = N_("Bindings"); o->subGroup = N_("Initiate Window Picker for All Workspaces"); o->displayHints = ""; o->shortDesc = N_("Initiate Window Picker for All Workspaces"); o->longDesc = N_("Layout and start transforming Windows from All Workspaces."); o->type = CompOptionTypeAction; o->value.action.initiate = scaleInitiateAll; o->value.action.terminate = scaleTerminate; o->value.action.bell = FALSE; o->value.action.edgeMask = (1 << SCREEN_EDGE_TOPRIGHT); o->value.action.state = CompActionStateInitEdge; o->value.action.state |= CompActionStateInitEdgeDnd; o->value.action.type = CompBindingTypeKey; o->value.action.state |= CompActionStateInitKey; o->value.action.state |= CompActionStateInitButton; o->value.action.key.modifiers = SCALE_INITIATE_ALL_MODIFIERS_DEFAULT; o->value.action.key.keysym = XStringToKeysym(SCALE_INITIATE_ALL_KEY_DEFAULT); o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_CURRENT_HEAD]; o->advanced = False; o->name = "initiate_current_head"; o->group = N_("Bindings"); o->subGroup = N_("Initiate current head scale only"); o->displayHints = ""; o->shortDesc = N_("Initiate Current Head Scale Only"); o->longDesc = N_("Only Scale windows on the Current Monitor."); o->type = CompOptionTypeAction; o->value.action.initiate = scaleInitiateCurrentHead; o->value.action.terminate = scaleTerminate; o->value.action.bell = FALSE; o->value.action.edgeMask = 0; o->value.action.state = CompActionStateInitEdge; o->value.action.state |= CompActionStateInitEdgeDnd; o->value.action.type = CompBindingTypeKey; o->value.action.state |= CompActionStateInitKey; o->value.action.state |= CompActionStateInitButton; o->value.action.key.modifiers = SCALE_INITIATE_CURRENT_HEAD_MODIFIERS_DEFAULT; o->value.action.key.keysym = XStringToKeysym(SCALE_INITIATE_CURRENT_HEAD_KEY_DEFAULT); } static CompOption *scaleGetDisplayOptions(CompDisplay * display, int *count) { if (display) { SCALE_DISPLAY(display); *count = NUM_OPTIONS(sd); return sd->opt; } else { ScaleDisplay *sd = malloc(sizeof(ScaleDisplay)); scaleDisplayInitOptions(sd); *count = NUM_OPTIONS(sd); return sd->opt; } } static Bool scaleInitDisplay(CompPlugin * p, CompDisplay * d) { ScaleDisplay *sd; sd = malloc(sizeof(ScaleDisplay)); if (!sd) return FALSE; sd->screenPrivateIndex = allocateScreenPrivateIndex(d); if (sd->screenPrivateIndex < 0) { free(sd); return FALSE; } sd->lastActiveNum = None; scaleDisplayInitOptions(sd); sd->leftKeyCode = XKeysymToKeycode(d->display, XStringToKeysym("Left")); sd->rightKeyCode = XKeysymToKeycode(d->display, XStringToKeysym("Right")); sd->upKeyCode = XKeysymToKeycode(d->display, XStringToKeysym("Up")); sd->downKeyCode = XKeysymToKeycode(d->display, XStringToKeysym("Down")); WRAP(sd, d, handleEvent, scaleHandleEvent); d->privates[displayPrivateIndex].ptr = sd; return TRUE; } static void scaleFiniDisplay(CompPlugin * p, CompDisplay * d) { SCALE_DISPLAY(d); freeScreenPrivateIndex(d, sd->screenPrivateIndex); UNWRAP(sd, d, handleEvent); free(sd); } static Bool scaleInitScreen(CompPlugin * p, CompScreen * s) { ScaleScreen *ss; SCALE_DISPLAY(s->display); ss = malloc(sizeof(ScaleScreen)); if (!ss) return FALSE; ss->clicked = FALSE; ss->windowPrivateIndex = allocateWindowPrivateIndex(s); if (ss->windowPrivateIndex < 0) { free(ss); return FALSE; } ss->grab = FALSE; ss->grabIndex = 0; ss->hoverHandle = 0; ss->dndTarget = None; ss->state = SCALE_STATE_NONE; ss->useClass = SCALE_USE_CLASS_DEFAULT; ss->slots = 0; ss->slotsSize = 0; ss->windows = 0; ss->windowsSize = 0; ss->line = 0; ss->lineSize = 0; ss->scale = 1.0f; ss->spacing = SCALE_SPACING_DEFAULT; ss->speed = SCALE_SPEED_DEFAULT; ss->timestep = SCALE_TIMESTEP_DEFAULT; ss->opacity = (OPAQUE * SCALE_OPACITY_DEFAULT) / 100; ss->darkenBackFactor = SCALE_DARKEN_BACK_FACTOR_DEFAULT; ss->onlyCurrent = FALSE; ss->currentWindow = 0; ss->selectedWindow = 0; ss->allScreensMode = FALSE; ss->darkenBack = SCALE_DARKEN_BACK_DEFAULT; ss->currentHead = FALSE; ss->head = 0; scaleSetHead(ss, s, SCALE_HEAD_DEFAULT); ss->iconOverlay = ScaleIconEmblem; ss->scaleMethod = SCALE_METHOD_INT_DEFAULT; ss->mmMode = MULTIMONITOR_MODE_DEFAULT; ss->scaleStateAtom = IPCS_GetAtom(IPCS_OBJECT(s), IPCS_VPTR, "SCALE_STATE_INT_PTR", TRUE); IPCS_SetVPtr(IPCS_OBJECT(s), ss->scaleStateAtom, (void *)&ss->state); scaleScreenInitOptions(ss); addScreenAction(s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_NORMAL]. value.action); addScreenAction(s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_APP].value.action); addScreenAction(s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_ALL].value.action); addScreenAction(s, &sd-> opt[SCALE_DISPLAY_OPTION_INITIATE_CURRENT_HEAD]. value.action); WRAP(ss, s, preparePaintScreen, scalePreparePaintScreen); WRAP(ss, s, donePaintScreen, scaleDonePaintScreen); WRAP(ss, s, paintScreen, scalePaintScreen); WRAP(ss, s, paintWindow, scalePaintWindow); WRAP(ss, s, damageWindowRect, scaleDamageWindowRect); ss->cursor = XCreateFontCursor(s->display->display, XC_left_ptr); s->privates[sd->screenPrivateIndex].ptr = ss; return TRUE; } static void scaleFiniScreen(CompPlugin * p, CompScreen * s) { SCALE_SCREEN(s); SCALE_DISPLAY(s->display); UNWRAP(ss, s, preparePaintScreen); UNWRAP(ss, s, donePaintScreen); UNWRAP(ss, s, paintScreen); UNWRAP(ss, s, paintWindow); UNWRAP(ss, s, damageWindowRect); removeScreenAction(s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_NORMAL]. value.action); removeScreenAction(s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_APP]. value.action); removeScreenAction(s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_ALL]. value.action); removeScreenAction(s, &sd-> opt[SCALE_DISPLAY_OPTION_INITIATE_CURRENT_HEAD]. value.action); IPCS_Unset(IPCS_OBJECT(s), ss->scaleStateAtom); if (ss->slotsSize) free(ss->slots); if (ss->lineSize) free(ss->line); if (ss->windowsSize) free(ss->windows); XFreeCursor(s->display->display, ss->cursor); freeWindowPrivateIndex(s, ss->windowPrivateIndex); free(ss); } static Bool scaleInitWindow(CompPlugin * p, CompWindow * w) { ScaleWindow *sw; SCALE_SCREEN(w->screen); sw = malloc(sizeof(ScaleWindow)); if (!sw) return FALSE; sw->animationAtom = IPCS_GetAtom(IPCS_OBJECT(w), IPCS_BOOL, "IS_ANIMATED", True); sw->slot = 0; sw->scale = 1.0f; sw->tx = sw->ty = 0.0f; sw->adjust = FALSE; sw->xVelocity = sw->yVelocity = 0.0f; sw->scaleVelocity = 1.0f; sw->delta = 1.0f; sw->rescaled = FALSE; sw->wasMinimized = FALSE; sw->oldScale = -1.0f; w->privates[ss->windowPrivateIndex].ptr = sw; setWinPort(w); return TRUE; } static void scaleFiniWindow(CompPlugin * p, CompWindow * w) { SCALE_WINDOW(w); free(sw); } static Bool scaleInit(CompPlugin * p) { displayPrivateIndex = allocateDisplayPrivateIndex(); if (displayPrivateIndex < 0) return FALSE; return TRUE; } static void scaleFini(CompPlugin * p) { if (displayPrivateIndex >= 0) freeDisplayPrivateIndex(displayPrivateIndex); } CompPluginDep setDeps[] = { {CompPluginRuleAfter, "decoration"} , {CompPluginRuleAfter, "animation"} , {CompPluginRuleAfter, "cube"} , {CompPluginRuleAfter, "wobbly"} , {CompPluginRuleAfter, "fade"} , }; CompPluginVTable scaleVTable = { "scale", N_("Scale"), N_("Scale windows"), scaleInit, scaleFini, scaleInitDisplay, scaleFiniDisplay, scaleInitScreen, scaleFiniScreen, scaleInitWindow, scaleFiniWindow, scaleGetDisplayOptions, scaleSetDisplayOption, scaleGetScreenOptions, scaleSetScreenOption, setDeps, sizeof(setDeps) / sizeof(setDeps[0]), 0, 0, BERYL_ABI_INFO, "beryl-plugins", "wm", 0, 0, True, }; CompPluginVTable *getCompPluginInfo(void) { return &scaleVTable; }