1 /*
2 * Compiz Fusion Grid plugin
3 *
4 * Copyright (c) 2008 Stephen Kennedy <suasol@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * Description:
17 *
18 * Plugin to act like winsplit revolution (http://www.winsplit-revolution.com/)
19 * use <Control><Alt>NUMPAD_KEY to move and tile your windows.
20 *
21 * Press the tiling keys several times to cycle through some tiling options.
22 */
23
24 #include <compiz-core.h>
25 #include <string.h>
26 #include "grid_options.h"
27
28 #define GRID_DEBUG 0
29
30 #if GRID_DEBUG
31 # include <stdio.h>
32 static FILE* gridOut;
33 # define DEBUG_RECT(VAR) fprintf(gridOut, #VAR " %i %i %i %i\n", VAR.x, VAR.y, VAR.width, VAR.height)
34 # define DEBUG_PRINT(ARGS) fprintf ARGS
35 #else
36 # define DEBUG_RECT(VAR)
37 # define DEBUG_PRINT(ARGS)
38 #endif
39
40 typedef enum
41 {
42 GridUnknown = 0,
43 GridBottomLeft = 1,
44 GridBottom = 2,
45 GridBottomRight = 3,
46 GridLeft = 4,
47 GridCenter = 5,
48 GridRight = 6,
49 GridTopLeft = 7,
50 GridTop = 8,
51 GridTopRight = 9,
52 } GridType;
53
54 typedef struct _GridProps
55 {
56 int gravityRight;
57 int gravityDown;
58 int numCellsX;
59 int numCellsY;
60 } GridProps;
61
62 static const GridProps gridProps[] =
63 {
64 {0,1, 1,1},
65
66 {0,1, 2,2},
67 {0,1, 1,2},
68 {1,1, 2,2},
69
70 {0,0, 2,1},
71 {0,0, 1,1},
72 {1,0, 2,1},
73
74 {0,0, 2,2},
75 {0,0, 1,2},
76 {1,0, 2,2},
77 };
78
79 static void
slotToRect(CompWindow * w,XRectangle * slot,XRectangle * rect)80 slotToRect (CompWindow *w,
81 XRectangle *slot,
82 XRectangle *rect)
83 {
84 rect->x = slot->x + w->input.left;
85 rect->y = slot->y + w->input.top;
86 rect->width = slot->width - (w->input.left + w->input.right);
87 rect->height = slot->height - (w->input.top + w->input.bottom);
88 }
89
90 static void
constrainSize(CompWindow * w,XRectangle * slot,XRectangle * rect)91 constrainSize (CompWindow *w,
92 XRectangle *slot,
93 XRectangle *rect)
94 {
95 XRectangle workarea;
96 XRectangle r;
97 int cw, ch;
98
99 getWorkareaForOutput (w->screen, outputDeviceForWindow (w), &workarea);
100 slotToRect (w, slot, &r);
101
102 if (constrainNewWindowSize (w, r.width, r.height, &cw, &ch))
103 {
104 /* constrained size may put window offscreen, adjust for that case */
105 int dx = r.x + cw - workarea.width - workarea.x + w->input.right;
106 int dy = r.y + ch - workarea.height - workarea.y + w->input.bottom;
107
108 if ( dx > 0 )
109 r.x -= dx;
110 if ( dy > 0 )
111 r.y -= dy;
112
113 r.width = cw;
114 r.height = ch;
115 }
116
117 *rect = r;
118 }
119
120 static Bool
gridCommon(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption,GridType where)121 gridCommon (CompDisplay *d,
122 CompAction *action,
123 CompActionState state,
124 CompOption *option,
125 int nOption,
126 GridType where)
127 {
128 Window xid;
129 CompWindow *cw;
130
131 xid = getIntOptionNamed (option, nOption, "window", 0);
132 cw = findWindowAtDisplay (d, xid);
133 if (cw)
134 {
135 XRectangle workarea;
136 XRectangle desiredSlot;
137 XRectangle desiredRect;
138 XRectangle currentRect;
139 GridProps props = gridProps[where];
140 XWindowChanges xwc;
141
142 DEBUG_PRINT ((gridOut, "\nPressed KP_%i\n", where));
143
144 /* get current available area */
145 getWorkareaForOutput (cw->screen, outputDeviceForWindow(cw), &workarea);
146 DEBUG_RECT (workarea);
147
148 /* Convention:
149 * xxxSlot include decorations (it's the screen area occupied)
150 * xxxRect are undecorated (it's the constrained position
151 of the contents)
152 */
153
154 /* slice and dice to get desired slot - including decorations */
155 desiredSlot.y = workarea.y + props.gravityDown *
156 (workarea.height / props.numCellsY);
157 desiredSlot.height = workarea.height / props.numCellsY;
158 desiredSlot.x = workarea.x + props.gravityRight *
159 (workarea.width / props.numCellsX);
160 desiredSlot.width = workarea.width / props.numCellsX;
161 DEBUG_RECT (desiredSlot);
162
163 /* Adjust for constraints and decorations */
164 constrainSize (cw, &desiredSlot, &desiredRect);
165 DEBUG_RECT (desiredRect);
166
167 /* Get current rect not including decorations */
168 currentRect.x = cw->serverX;
169 currentRect.y = cw->serverY;
170 currentRect.width = cw->serverWidth;
171 currentRect.height = cw->serverHeight;
172 DEBUG_RECT (currentRect);
173
174 if (desiredRect.y == currentRect.y &&
175 desiredRect.height == currentRect.height)
176 {
177 int slotWidth33 = workarea.width / 3;
178 int slotWidth66 = workarea.width - slotWidth33;
179
180 DEBUG_PRINT ((gridOut, "Multi!\n"));
181
182 if (props.numCellsX == 2) /* keys (1, 4, 7, 3, 6, 9) */
183 {
184 if (currentRect.width == desiredRect.width &&
185 currentRect.x == desiredRect.x)
186 {
187 desiredSlot.width = slotWidth66;
188 desiredSlot.x = workarea.x +
189 props.gravityRight * slotWidth33;
190 }
191 else
192 {
193 /* tricky, have to allow for window constraints when
194 * computing what the 33% and 66% offsets would be
195 */
196 XRectangle rect33, rect66, slot33, slot66;
197
198 slot33 = desiredSlot;
199 slot33.x = workarea.x + props.gravityRight * slotWidth66;
200 slot33.width = slotWidth33;
201 constrainSize (cw, &slot33, &rect33);
202 DEBUG_RECT (slot33);
203 DEBUG_RECT (rect33);
204
205 slot66 = desiredSlot;
206 slot66.x = workarea.x + props.gravityRight * slotWidth33;
207 slot66.width = slotWidth66;
208 constrainSize (cw, &slot66, &rect66);
209 DEBUG_RECT (slot66);
210 DEBUG_RECT (rect66);
211
212 if (currentRect.width == rect66.width &&
213 currentRect.x == rect66.x)
214 {
215 desiredSlot.width = slotWidth33;
216 desiredSlot.x = workarea.x +
217 props.gravityRight * slotWidth66;
218 }
219 }
220 }
221 else /* keys (2, 5, 8) */
222 {
223 if (currentRect.width == desiredRect.width &&
224 currentRect.x == desiredRect.x)
225 {
226 desiredSlot.width = slotWidth33;
227 desiredSlot.x = workarea.x + slotWidth33;
228 }
229 }
230 constrainSize (cw, &desiredSlot, &desiredRect);
231 DEBUG_RECT (desiredRect);
232 }
233
234 xwc.x = desiredRect.x;
235 xwc.y = desiredRect.y;
236 xwc.width = desiredRect.width;
237 xwc.height = desiredRect.height;
238
239 if (cw->mapNum)
240 sendSyncRequest (cw);
241
242 if (cw->state & MAXIMIZE_STATE)
243 {
244 /* maximized state interferes with us, clear it */
245 maximizeWindow (cw, 0);
246 }
247
248 /* TODO: animate move+resize */
249 configureXWindow (cw, CWX | CWY | CWWidth | CWHeight, &xwc);
250 }
251
252 return TRUE;
253 }
254
255 #define HANDLER(WHERE) \
256 static Bool \
257 grid##WHERE(CompDisplay *d, \
258 CompAction *action, \
259 CompActionState state, \
260 CompOption *option, \
261 int nOption) \
262 { \
263 return gridCommon (d, action, state, \
264 option, nOption, Grid##WHERE); \
265 }
266
267 HANDLER (BottomLeft)
HANDLER(Bottom)268 HANDLER (Bottom)
269 HANDLER (BottomRight)
270 HANDLER (Left)
271 HANDLER (Center)
272 HANDLER (Right)
273 HANDLER (TopLeft)
274 HANDLER (Top)
275 HANDLER (TopRight)
276
277 #undef HANDLER
278
279 /* Configuration, initialization, boring stuff. */
280
281 static Bool
282 gridInitDisplay (CompPlugin *p,
283 CompDisplay *d)
284 {
285 if (!checkPluginABI ("core", CORE_ABIVERSION))
286 return FALSE;
287
288 gridSetPutCenterKeyInitiate (d, gridCenter);
289 gridSetPutLeftKeyInitiate (d, gridLeft);
290 gridSetPutRightKeyInitiate (d, gridRight);
291 gridSetPutTopKeyInitiate (d, gridTop);
292 gridSetPutBottomKeyInitiate (d, gridBottom);
293 gridSetPutTopleftKeyInitiate (d, gridTopLeft);
294 gridSetPutToprightKeyInitiate (d, gridTopRight);
295 gridSetPutBottomleftKeyInitiate (d, gridBottomLeft);
296 gridSetPutBottomrightKeyInitiate (d, gridBottomRight);
297
298 return TRUE;
299 }
300
301 static CompBool
gridInitObject(CompPlugin * p,CompObject * o)302 gridInitObject (CompPlugin *p,
303 CompObject *o)
304 {
305 static InitPluginObjectProc dispTab[] = {
306 (InitPluginObjectProc) 0, /* InitCore */
307 (InitPluginObjectProc) gridInitDisplay
308 };
309
310 RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
311 }
312
313 static Bool
gridInitPlugin(CompPlugin * p)314 gridInitPlugin (CompPlugin *p)
315 {
316 #if GRID_DEBUG
317 gridOut = fopen("/tmp/grid.log", "w");
318 setlinebuf(gridOut);
319 #endif
320
321 return TRUE;
322 }
323
324 static void
gridFiniPlugin(CompPlugin * p)325 gridFiniPlugin (CompPlugin *p)
326 {
327 #if GRID_DEBUG
328 fclose(gridOut);
329 gridOut = NULL;
330 #endif
331 }
332
333 CompPluginVTable gridVTable =
334 {
335 "grid",
336 0,
337 gridInitPlugin,
338 gridFiniPlugin,
339 gridInitObject,
340 0,
341 0,
342 0
343 };
344
345 CompPluginVTable *
getCompPluginInfo()346 getCompPluginInfo ()
347 {
348 return &gridVTable;
349 }
350