/*
* 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 <davidr@novell.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <beryl.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1