Change to the linux kernel coding style
[wmaker-crm.git] / src / moveres.c
1 /*
2  *  Window Maker window manager
3  *
4  *  Copyright (c) 1997-2003 Alfredo K. Kojima
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (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  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19  *  USA.
20  */
21
22 #include "wconfig.h"
23
24 #include <X11/Xlib.h>
25 #include <X11/Xutil.h>
26 #include <X11/keysym.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31
32 #include "WindowMaker.h"
33 #include "wcore.h"
34 #include "framewin.h"
35 #include "window.h"
36 #include "client.h"
37 #include "icon.h"
38 #include "dock.h"
39 #include "funcs.h"
40 #include "actions.h"
41 #include "workspace.h"
42
43 #include "geomview.h"
44 #include "screen.h"
45 #include "xinerama.h"
46
47 #include <WINGs/WINGsP.h>
48
49 /* How many different types of geometry/position
50  display thingies are there? */
51 #define NUM_DISPLAYS 5
52
53 #define LEFT            1
54 #define RIGHT           2
55 #define HORIZONTAL      (LEFT|RIGHT)
56 #define UP              4
57 #define DOWN            8
58 #define VERTICAL        (UP|DOWN)
59
60 /* True if window currently has a border. This also includes borderless
61  * windows which are currently selected
62  */
63 #define HAS_BORDER_WITH_SELECT(w) ((w)->flags.selected || HAS_BORDER(w))
64
65 /****** Global Variables ******/
66 extern Time LastTimestamp;
67
68 extern Cursor wCursor[WCUR_LAST];
69
70 extern WPreferences wPreferences;
71
72 extern Atom _XA_WM_PROTOCOLS;
73
74 /*
75  *----------------------------------------------------------------------
76  * checkMouseSamplingRate-
77  *      For lowering the mouse motion sampling rate for machines where
78  * it's too high (SGIs). If it returns False then the event should be
79  * ignored.
80  *----------------------------------------------------------------------
81  */
82 static Bool checkMouseSamplingRate(XEvent * ev)
83 {
84         static Time previousMotion = 0;
85
86         if (ev->type == MotionNotify) {
87                 if (ev->xmotion.time - previousMotion < DELAY_BETWEEN_MOUSE_SAMPLING) {
88                         return False;
89                 } else {
90                         previousMotion = ev->xmotion.time;
91                 }
92         }
93         return True;
94 }
95
96 /*
97  *----------------------------------------------------------------------
98  * moveGeometryDisplayCentered
99  *
100  * routine that moves the geometry/position window on scr so it is
101  * centered over the given coordinates (x,y). Also the window position
102  * is clamped so it stays on the screen at all times.
103  *----------------------------------------------------------------------
104  */
105 static void moveGeometryDisplayCentered(WScreen * scr, int x, int y)
106 {
107         unsigned int w = WMWidgetWidth(scr->gview);
108         unsigned int h = WMWidgetHeight(scr->gview);
109         int x1 = 0, y1 = 0, x2 = scr->scr_width, y2 = scr->scr_height;
110
111         x -= w / 2;
112         y -= h / 2;
113
114         /* dead area check */
115         if (scr->xine_info.count) {
116                 WMRect rect;
117                 int head, flags;
118
119                 rect.pos.x = x;
120                 rect.pos.y = y;
121                 rect.size.width = w;
122                 rect.size.height = h;
123
124                 head = wGetRectPlacementInfo(scr, rect, &flags);
125
126                 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL)) {
127                         rect = wGetRectForHead(scr, head);
128                         x1 = rect.pos.x;
129                         y1 = rect.pos.y;
130                         x2 = x1 + rect.size.width;
131                         y2 = y1 + rect.size.height;
132                 }
133         }
134
135         if (x < x1 + 1)
136                 x = x1 + 1;
137         else if (x > (x2 - w))
138                 x = x2 - w;
139
140         if (y < y1 + 1)
141                 y = y1 + 1;
142         else if (y > (y2 - h))
143                 y = y2 - h;
144
145         WMMoveWidget(scr->gview, x, y);
146 }
147
148 static void showPosition(WWindow * wwin, int x, int y)
149 {
150         WScreen *scr = wwin->screen_ptr;
151
152         if (wPreferences.move_display == WDIS_NEW) {
153 #if 0
154                 int width = wwin->frame->core->width;
155                 int height = wwin->frame->core->height;
156
157                 GC lgc = scr->line_gc;
158                 XSetForeground(dpy, lgc, scr->line_pixel);
159                 sprintf(num, "%i", x);
160
161                 XDrawLine(dpy, scr->root_win, lgc, 0, y - 1, scr->scr_width, y - 1);
162                 XDrawLine(dpy, scr->root_win, lgc, 0, y + height + 2, scr->scr_width, y + height + 2);
163                 XDrawLine(dpy, scr->root_win, lgc, x - 1, 0, x - 1, scr->scr_height);
164                 XDrawLine(dpy, scr->root_win, lgc, x + width + 2, 0, x + width + 2, scr->scr_height);
165 #endif
166         } else {
167 #ifdef VIRTUAL_DESKTOP
168                 WSetGeometryViewShownPosition(scr->gview,
169                                               x + scr->workspaces[scr->current_workspace]->view_x,
170                                               y + scr->workspaces[scr->current_workspace]->view_y);
171 #else
172                 WSetGeometryViewShownPosition(scr->gview, x, y);
173 #endif
174         }
175 }
176
177 static void cyclePositionDisplay(WWindow * wwin, int x, int y, int w, int h)
178 {
179         WScreen *scr = wwin->screen_ptr;
180         WMRect rect;
181
182         wPreferences.move_display++;
183         wPreferences.move_display %= NUM_DISPLAYS;
184
185         if (wPreferences.move_display == WDIS_NEW) {
186                 wPreferences.move_display++;
187                 wPreferences.move_display %= NUM_DISPLAYS;
188         }
189
190         if (wPreferences.move_display == WDIS_NONE) {
191                 WMUnmapWidget(scr->gview);
192         } else {
193                 if (wPreferences.move_display == WDIS_CENTER) {
194                         rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
195                         moveGeometryDisplayCentered(scr, rect.pos.x + rect.size.width / 2,
196                                                     rect.pos.y + rect.size.height / 2);
197                 } else if (wPreferences.move_display == WDIS_TOPLEFT) {
198                         rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
199                         moveGeometryDisplayCentered(scr, rect.pos.x + 1, rect.pos.y + 1);
200                 } else if (wPreferences.move_display == WDIS_FRAME_CENTER) {
201                         moveGeometryDisplayCentered(scr, x + w / 2, y + h / 2);
202                 }
203                 WMMapWidget(scr->gview);
204         }
205 }
206
207 static void mapPositionDisplay(WWindow * wwin, int x, int y, int w, int h)
208 {
209         WScreen *scr = wwin->screen_ptr;
210         WMRect rect;
211
212         if (wPreferences.move_display == WDIS_NEW || wPreferences.move_display == WDIS_NONE) {
213                 return;
214         } else if (wPreferences.move_display == WDIS_CENTER) {
215                 rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
216                 moveGeometryDisplayCentered(scr, rect.pos.x + rect.size.width / 2,
217                                             rect.pos.y + rect.size.height / 2);
218         } else if (wPreferences.move_display == WDIS_TOPLEFT) {
219                 rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
220                 moveGeometryDisplayCentered(scr, rect.pos.x + 1, rect.pos.y + 1);
221         } else if (wPreferences.move_display == WDIS_FRAME_CENTER) {
222                 moveGeometryDisplayCentered(scr, x + w / 2, y + h / 2);
223         }
224         WMMapWidget(scr->gview);
225         WSetGeometryViewShownPosition(scr->gview, x, y);
226 }
227
228 static void showGeometry(WWindow * wwin, int x1, int y1, int x2, int y2, int direction)
229 {
230         WScreen *scr = wwin->screen_ptr;
231         Window root = scr->root_win;
232         GC gc = scr->line_gc;
233         int ty, by, my, x, y, mx, s;
234         char num[16];
235         XSegment segment[4];
236         int fw, fh;
237
238         /* This seems necessary for some odd reason (too lazy to write x1-1 and
239          * x2-1 everywhere below in the code). But why only for x? */
240         x1--;
241         x2--;
242
243         if (HAS_BORDER_WITH_SELECT(wwin)) {
244                 x1 += FRAME_BORDER_WIDTH;
245                 x2 += FRAME_BORDER_WIDTH;
246                 y1 += FRAME_BORDER_WIDTH;
247                 y2 += FRAME_BORDER_WIDTH;
248         }
249
250         ty = y1 + wwin->frame->top_width;
251         by = y2 - wwin->frame->bottom_width;
252
253         if (wPreferences.size_display == WDIS_NEW) {
254                 fw = XTextWidth(scr->tech_draw_font, "8888", 4);
255                 fh = scr->tech_draw_font->ascent + scr->tech_draw_font->descent;
256
257                 XSetForeground(dpy, gc, scr->line_pixel);
258
259                 /* vertical geometry */
260                 if (((direction & LEFT) && (x2 < scr->scr_width - fw)) || (x1 < fw)) {
261                         x = x2;
262                         s = -15;
263                 } else {
264                         x = x1;
265                         s = 15;
266                 }
267                 my = (ty + by) / 2;
268
269                 /* top arrow & end bar */
270                 segment[0].x1 = x - (s + 6);
271                 segment[0].y1 = ty;
272                 segment[0].x2 = x - (s - 10);
273                 segment[0].y2 = ty;
274
275                 /* arrowhead */
276                 segment[1].x1 = x - (s - 2);
277                 segment[1].y1 = ty + 1;
278                 segment[1].x2 = x - (s - 5);
279                 segment[1].y2 = ty + 7;
280
281                 segment[2].x1 = x - (s - 2);
282                 segment[2].y1 = ty + 1;
283                 segment[2].x2 = x - (s + 1);
284                 segment[2].y2 = ty + 7;
285
286                 /* line */
287                 segment[3].x1 = x - (s - 2);
288                 segment[3].y1 = ty + 1;
289                 segment[3].x2 = x - (s - 2);
290                 segment[3].y2 = my - fh / 2 - 1;
291
292                 XDrawSegments(dpy, root, gc, segment, 4);
293
294                 /* bottom arrow & end bar */
295                 segment[0].y1 = by;
296                 segment[0].y2 = by;
297
298                 /* arrowhead */
299                 segment[1].y1 = by - 1;
300                 segment[1].y2 = by - 7;
301
302                 segment[2].y1 = by - 1;
303                 segment[2].y2 = by - 7;
304
305                 /* line */
306                 segment[3].y1 = my + fh / 2 + 2;
307                 segment[3].y2 = by - 1;
308
309                 XDrawSegments(dpy, root, gc, segment, 4);
310
311                 snprintf(num, sizeof(num), "%i", (by - ty - wwin->normal_hints->base_height) /
312                          wwin->normal_hints->height_inc);
313                 fw = XTextWidth(scr->tech_draw_font, num, strlen(num));
314
315                 /* Display the height. */
316                 XSetFont(dpy, gc, scr->tech_draw_font->fid);
317                 XDrawString(dpy, root, gc, x - s + 3 - fw / 2, my + scr->tech_draw_font->ascent - fh / 2 + 1, num,
318                             strlen(num));
319
320                 /* horizontal geometry */
321                 if (y1 < 15) {
322                         y = y2;
323                         s = -15;
324                 } else {
325                         y = y1;
326                         s = 15;
327                 }
328                 mx = x1 + (x2 - x1) / 2;
329                 snprintf(num, sizeof(num), "%i", (x2 - x1 - wwin->normal_hints->base_width) /
330                          wwin->normal_hints->width_inc);
331                 fw = XTextWidth(scr->tech_draw_font, num, strlen(num));
332
333                 /* left arrow & end bar */
334                 segment[0].x1 = x1;
335                 segment[0].y1 = y - (s + 6);
336                 segment[0].x2 = x1;
337                 segment[0].y2 = y - (s - 10);
338
339                 /* arrowhead */
340                 segment[1].x1 = x1 + 7;
341                 segment[1].y1 = y - (s + 1);
342                 segment[1].x2 = x1 + 1;
343                 segment[1].y2 = y - (s - 2);
344
345                 segment[2].x1 = x1 + 1;
346                 segment[2].y1 = y - (s - 2);
347                 segment[2].x2 = x1 + 7;
348                 segment[2].y2 = y - (s - 5);
349
350                 /* line */
351                 segment[3].x1 = x1 + 1;
352                 segment[3].y1 = y - (s - 2);
353                 segment[3].x2 = mx - fw / 2 - 2;
354                 segment[3].y2 = y - (s - 2);
355
356                 XDrawSegments(dpy, root, gc, segment, 4);
357
358                 /* right arrow & end bar */
359                 segment[0].x1 = x2 + 1;
360                 segment[0].x2 = x2 + 1;
361
362                 /* arrowhead */
363                 segment[1].x1 = x2 - 6;
364                 segment[1].x2 = x2;
365
366                 segment[2].x1 = x2;
367                 segment[2].x2 = x2 - 6;
368
369                 /* line */
370                 segment[3].x1 = mx + fw / 2 + 2;
371                 segment[3].x2 = x2;
372
373                 XDrawSegments(dpy, root, gc, segment, 4);
374
375                 /* Display the width. */
376                 XDrawString(dpy, root, gc, mx - fw / 2 + 1, y - s + scr->tech_draw_font->ascent - fh / 2 + 1, num,
377                             strlen(num));
378         } else {
379                 WSetGeometryViewShownSize(scr->gview, (x2 - x1 - wwin->normal_hints->base_width)
380                                           / wwin->normal_hints->width_inc,
381                                           (by - ty - wwin->normal_hints->base_height)
382                                           / wwin->normal_hints->height_inc);
383         }
384 }
385
386 static void cycleGeometryDisplay(WWindow * wwin, int x, int y, int w, int h, int dir)
387 {
388         WScreen *scr = wwin->screen_ptr;
389         WMRect rect;
390
391         wPreferences.size_display++;
392         wPreferences.size_display %= NUM_DISPLAYS;
393
394         if (wPreferences.size_display == WDIS_NEW || wPreferences.size_display == WDIS_NONE) {
395                 WMUnmapWidget(scr->gview);
396         } else {
397                 if (wPreferences.size_display == WDIS_CENTER) {
398                         rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
399                         moveGeometryDisplayCentered(scr, rect.pos.x + rect.size.width / 2,
400                                                     rect.pos.y + rect.size.height / 2);
401                 } else if (wPreferences.size_display == WDIS_TOPLEFT) {
402                         rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
403                         moveGeometryDisplayCentered(scr, rect.pos.x + 1, rect.pos.y + 1);
404                 } else if (wPreferences.size_display == WDIS_FRAME_CENTER) {
405                         moveGeometryDisplayCentered(scr, x + w / 2, y + h / 2);
406                 }
407                 WMMapWidget(scr->gview);
408                 showGeometry(wwin, x, y, x + w, y + h, dir);
409         }
410 }
411
412 static void mapGeometryDisplay(WWindow * wwin, int x, int y, int w, int h)
413 {
414         WScreen *scr = wwin->screen_ptr;
415         WMRect rect;
416
417         if (wPreferences.size_display == WDIS_NEW || wPreferences.size_display == WDIS_NONE)
418                 return;
419
420         if (wPreferences.size_display == WDIS_CENTER) {
421                 rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
422                 moveGeometryDisplayCentered(scr, rect.pos.x + rect.size.width / 2,
423                                             rect.pos.y + rect.size.height / 2);
424         } else if (wPreferences.size_display == WDIS_TOPLEFT) {
425                 rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
426                 moveGeometryDisplayCentered(scr, rect.pos.x + 1, rect.pos.y + 1);
427         } else if (wPreferences.size_display == WDIS_FRAME_CENTER) {
428                 moveGeometryDisplayCentered(scr, x + w / 2, y + h / 2);
429         }
430         WMMapWidget(scr->gview);
431         showGeometry(wwin, x, y, x + w, y + h, 0);
432 }
433
434 static void doWindowMove(WWindow * wwin, WMArray * array, int dx, int dy)
435 {
436         WWindow *tmpw;
437         WScreen *scr = wwin->screen_ptr;
438         int x, y;
439
440         if (!array || !WMGetArrayItemCount(array)) {
441                 wWindowMove(wwin, wwin->frame_x + dx, wwin->frame_y + dy);
442         } else {
443                 WMArrayIterator iter;
444
445                 WM_ITERATE_ARRAY(array, tmpw, iter) {
446                         x = tmpw->frame_x + dx;
447                         y = tmpw->frame_y + dy;
448
449 #if 1                           /* XXX: with xinerama patch was #if 0, check this */
450                         /* don't let windows become unreachable */
451
452                         if (x + (int)tmpw->frame->core->width < 20)
453                                 x = 20 - (int)tmpw->frame->core->width;
454                         else if (x + 20 > scr->scr_width)
455                                 x = scr->scr_width - 20;
456
457                         if (y + (int)tmpw->frame->core->height < 20)
458                                 y = 20 - (int)tmpw->frame->core->height;
459                         else if (y + 20 > scr->scr_height)
460                                 y = scr->scr_height - 20;
461 #else
462                         wScreenBringInside(scr, &x, &y,
463                                            (int)tmpw->frame->core->width, (int)tmpw->frame->core->height);
464 #endif
465
466                         wWindowMove(tmpw, x, y);
467                 }
468         }
469 }
470
471 static void drawTransparentFrame(WWindow * wwin, int x, int y, int width, int height)
472 {
473         Window root = wwin->screen_ptr->root_win;
474         GC gc = wwin->screen_ptr->frame_gc;
475         int h = 0;
476         int bottom = 0;
477
478         if (HAS_BORDER_WITH_SELECT(wwin)) {
479                 x += FRAME_BORDER_WIDTH;
480                 y += FRAME_BORDER_WIDTH;
481         }
482
483         if (HAS_TITLEBAR(wwin) && !wwin->flags.shaded) {
484                 h = WMFontHeight(wwin->screen_ptr->title_font) + (wPreferences.window_title_clearance +
485                                                                   TITLEBAR_EXTEND_SPACE) * 2;
486         }
487         if (HAS_RESIZEBAR(wwin) && !wwin->flags.shaded) {
488                 /* Can't use wwin-frame->bottom_width because, in some cases
489                    (e.g. interactive placement), frame does not point to anything. */
490                 bottom = RESIZEBAR_HEIGHT;
491         }
492         XDrawRectangle(dpy, root, gc, x - 1, y - 1, width + 1, height + 1);
493
494         if (h > 0) {
495                 XDrawLine(dpy, root, gc, x, y + h - 1, x + width, y + h - 1);
496         }
497         if (bottom > 0) {
498                 XDrawLine(dpy, root, gc, x, y + height - bottom, x + width, y + height - bottom);
499         }
500 }
501
502 static void drawFrames(WWindow * wwin, WMArray * array, int dx, int dy)
503 {
504         WWindow *tmpw;
505         int scr_width = wwin->screen_ptr->scr_width;
506         int scr_height = wwin->screen_ptr->scr_height;
507         int x, y;
508
509         if (!array) {
510
511                 x = wwin->frame_x + dx;
512                 y = wwin->frame_y + dy;
513
514                 drawTransparentFrame(wwin, x, y, wwin->frame->core->width, wwin->frame->core->height);
515
516         } else {
517                 WMArrayIterator iter;
518
519                 WM_ITERATE_ARRAY(array, tmpw, iter) {
520                         x = tmpw->frame_x + dx;
521                         y = tmpw->frame_y + dy;
522
523                         /* don't let windows become unreachable */
524 #if 1                           /* XXX: was 0 in XINERAMA patch, check */
525                         if (x + (int)tmpw->frame->core->width < 20)
526                                 x = 20 - (int)tmpw->frame->core->width;
527                         else if (x + 20 > scr_width)
528                                 x = scr_width - 20;
529
530                         if (y + (int)tmpw->frame->core->height < 20)
531                                 y = 20 - (int)tmpw->frame->core->height;
532                         else if (y + 20 > scr_height)
533                                 y = scr_height - 20;
534
535 #else
536                         wScreenBringInside(wwin->screen_ptr, &x, &y,
537                                            (int)tmpw->frame->core->width, (int)tmpw->frame->core->height);
538 #endif
539
540                         drawTransparentFrame(tmpw, x, y, tmpw->frame->core->width, tmpw->frame->core->height);
541                 }
542         }
543 }
544
545 static void flushMotion()
546 {
547         XEvent ev;
548
549         XSync(dpy, False);
550         while (XCheckMaskEvent(dpy, ButtonMotionMask, &ev)) ;
551 }
552
553 static void crossWorkspace(WScreen * scr, WWindow * wwin, int opaque_move, int new_workspace, int rewind)
554 {
555         /* do not let window be unmapped */
556         if (opaque_move) {
557                 wwin->flags.changing_workspace = 1;
558                 wWindowChangeWorkspace(wwin, new_workspace);
559         }
560         /* go to new workspace */
561         wWorkspaceChange(scr, new_workspace);
562
563         wwin->flags.changing_workspace = 0;
564
565         if (rewind)
566                 XWarpPointer(dpy, None, None, 0, 0, 0, 0, scr->scr_width - 20, 0);
567         else
568                 XWarpPointer(dpy, None, None, 0, 0, 0, 0, -(scr->scr_width - 20), 0);
569
570         flushMotion();
571
572         if (!opaque_move) {
573                 XGrabPointer(dpy, scr->root_win, True, PointerMotionMask
574                              | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
575                              GrabModeAsync, None, wCursor[WCUR_MOVE], CurrentTime);
576         }
577 }
578
579 typedef struct {
580         /* arrays of WWindows sorted by the respective border position */
581         WWindow **topList;      /* top border */
582         WWindow **leftList;     /* left border */
583         WWindow **rightList;    /* right border */
584         WWindow **bottomList;   /* bottom border */
585         int count;
586
587         /* index of window in the above lists indicating the relative position
588          * of the window with the others */
589         int topIndex;
590         int leftIndex;
591         int rightIndex;
592         int bottomIndex;
593
594         int rubCount;           /* for workspace switching */
595
596         int winWidth, winHeight;        /* width/height of the window */
597         int realX, realY;       /* actual position of the window */
598         int calcX, calcY;       /* calculated position of window */
599         int omouseX, omouseY;   /* old mouse position */
600         int mouseX, mouseY;     /* last known position of the pointer */
601 } MoveData;
602
603 #define WTOP(w) (w)->frame_y
604 #define WLEFT(w) (w)->frame_x
605 #define WRIGHT(w) ((w)->frame_x + (int)(w)->frame->core->width - 1 + \
606     (HAS_BORDER_WITH_SELECT(w) ? 2*FRAME_BORDER_WIDTH : 0))
607 #define WBOTTOM(w) ((w)->frame_y + (int)(w)->frame->core->height - 1 + \
608     (HAS_BORDER_WITH_SELECT(w) ? 2*FRAME_BORDER_WIDTH : 0))
609
610 static int compareWTop(const void *a, const void *b)
611 {
612         WWindow *wwin1 = *(WWindow **) a;
613         WWindow *wwin2 = *(WWindow **) b;
614
615         if (WTOP(wwin1) > WTOP(wwin2))
616                 return -1;
617         else if (WTOP(wwin1) < WTOP(wwin2))
618                 return 1;
619         else
620                 return 0;
621 }
622
623 static int compareWLeft(const void *a, const void *b)
624 {
625         WWindow *wwin1 = *(WWindow **) a;
626         WWindow *wwin2 = *(WWindow **) b;
627
628         if (WLEFT(wwin1) > WLEFT(wwin2))
629                 return -1;
630         else if (WLEFT(wwin1) < WLEFT(wwin2))
631                 return 1;
632         else
633                 return 0;
634 }
635
636 static int compareWRight(const void *a, const void *b)
637 {
638         WWindow *wwin1 = *(WWindow **) a;
639         WWindow *wwin2 = *(WWindow **) b;
640
641         if (WRIGHT(wwin1) < WRIGHT(wwin2))
642                 return -1;
643         else if (WRIGHT(wwin1) > WRIGHT(wwin2))
644                 return 1;
645         else
646                 return 0;
647 }
648
649 static int compareWBottom(const void *a, const void *b)
650 {
651         WWindow *wwin1 = *(WWindow **) a;
652         WWindow *wwin2 = *(WWindow **) b;
653
654         if (WBOTTOM(wwin1) < WBOTTOM(wwin2))
655                 return -1;
656         else if (WBOTTOM(wwin1) > WBOTTOM(wwin2))
657                 return 1;
658         else
659                 return 0;
660 }
661
662 static void updateResistance(WWindow * wwin, MoveData * data, int newX, int newY)
663 {
664         int i;
665         int newX2 = newX + data->winWidth;
666         int newY2 = newY + data->winHeight;
667         Bool ok = False;
668
669         if (newX < data->realX) {
670                 if (data->rightIndex > 0 && newX < WRIGHT(data->rightList[data->rightIndex - 1])) {
671                         ok = True;
672                 } else if (data->leftIndex <= data->count - 1 && newX2 <= WLEFT(data->leftList[data->leftIndex])) {
673                         ok = True;
674                 }
675         } else if (newX > data->realX) {
676                 if (data->leftIndex > 0 && newX2 > WLEFT(data->leftList[data->leftIndex - 1])) {
677                         ok = True;
678                 } else if (data->rightIndex <= data->count - 1
679                            && newX >= WRIGHT(data->rightList[data->rightIndex])) {
680                         ok = True;
681                 }
682         }
683
684         if (!ok) {
685                 if (newY < data->realY) {
686                         if (data->bottomIndex > 0 && newY < WBOTTOM(data->bottomList[data->bottomIndex - 1])) {
687                                 ok = True;
688                         } else if (data->topIndex <= data->count - 1
689                                    && newY2 <= WTOP(data->topList[data->topIndex])) {
690                                 ok = True;
691                         }
692                 } else if (newY > data->realY) {
693                         if (data->topIndex > 0 && newY2 > WTOP(data->topList[data->topIndex - 1])) {
694                                 ok = True;
695                         } else if (data->bottomIndex <= data->count - 1
696                                    && newY >= WBOTTOM(data->bottomList[data->bottomIndex])) {
697                                 ok = True;
698                         }
699                 }
700         }
701
702         if (!ok)
703                 return;
704
705         /* TODO: optimize this */
706         if (data->realY < WBOTTOM(data->bottomList[0])) {
707                 data->bottomIndex = 0;
708         }
709         if (data->realX < WRIGHT(data->rightList[0])) {
710                 data->rightIndex = 0;
711         }
712         if ((data->realX + data->winWidth) > WLEFT(data->leftList[0])) {
713                 data->leftIndex = 0;
714         }
715         if ((data->realY + data->winHeight) > WTOP(data->topList[0])) {
716                 data->topIndex = 0;
717         }
718         for (i = 0; i < data->count; i++) {
719                 if (data->realY > WBOTTOM(data->bottomList[i])) {
720                         data->bottomIndex = i + 1;
721                 }
722                 if (data->realX > WRIGHT(data->rightList[i])) {
723                         data->rightIndex = i + 1;
724                 }
725                 if ((data->realX + data->winWidth) < WLEFT(data->leftList[i])) {
726                         data->leftIndex = i + 1;
727                 }
728                 if ((data->realY + data->winHeight) < WTOP(data->topList[i])) {
729                         data->topIndex = i + 1;
730                 }
731         }
732 }
733
734 static void freeMoveData(MoveData * data)
735 {
736         if (data->topList)
737                 wfree(data->topList);
738         if (data->leftList)
739                 wfree(data->leftList);
740         if (data->rightList)
741                 wfree(data->rightList);
742         if (data->bottomList)
743                 wfree(data->bottomList);
744 }
745
746 static void updateMoveData(WWindow * wwin, MoveData * data)
747 {
748         WScreen *scr = wwin->screen_ptr;
749         WWindow *tmp;
750         int i;
751
752         data->count = 0;
753         tmp = scr->focused_window;
754         while (tmp) {
755                 if (tmp != wwin && scr->current_workspace == tmp->frame->workspace
756                     && !tmp->flags.miniaturized
757                     && !tmp->flags.hidden && !tmp->flags.obscured && !WFLAGP(tmp, sunken)) {
758                         data->topList[data->count] = tmp;
759                         data->leftList[data->count] = tmp;
760                         data->rightList[data->count] = tmp;
761                         data->bottomList[data->count] = tmp;
762                         data->count++;
763                 }
764                 tmp = tmp->prev;
765         }
766
767         if (data->count == 0) {
768                 data->topIndex = 0;
769                 data->leftIndex = 0;
770                 data->rightIndex = 0;
771                 data->bottomIndex = 0;
772                 return;
773         }
774
775         /* order from closest to the border of the screen to farthest */
776
777         qsort(data->topList, data->count, sizeof(WWindow **), compareWTop);
778         qsort(data->leftList, data->count, sizeof(WWindow **), compareWLeft);
779         qsort(data->rightList, data->count, sizeof(WWindow **), compareWRight);
780         qsort(data->bottomList, data->count, sizeof(WWindow **), compareWBottom);
781
782         /* figure the position of the window relative to the others */
783
784         data->topIndex = -1;
785         data->leftIndex = -1;
786         data->rightIndex = -1;
787         data->bottomIndex = -1;
788
789         if (WTOP(wwin) < WBOTTOM(data->bottomList[0])) {
790                 data->bottomIndex = 0;
791         }
792         if (WLEFT(wwin) < WRIGHT(data->rightList[0])) {
793                 data->rightIndex = 0;
794         }
795         if (WRIGHT(wwin) > WLEFT(data->leftList[0])) {
796                 data->leftIndex = 0;
797         }
798         if (WBOTTOM(wwin) > WTOP(data->topList[0])) {
799                 data->topIndex = 0;
800         }
801         for (i = 0; i < data->count; i++) {
802                 if (WTOP(wwin) >= WBOTTOM(data->bottomList[i])) {
803                         data->bottomIndex = i + 1;
804                 }
805                 if (WLEFT(wwin) >= WRIGHT(data->rightList[i])) {
806                         data->rightIndex = i + 1;
807                 }
808                 if (WRIGHT(wwin) <= WLEFT(data->leftList[i])) {
809                         data->leftIndex = i + 1;
810                 }
811                 if (WBOTTOM(wwin) <= WTOP(data->topList[i])) {
812                         data->topIndex = i + 1;
813                 }
814         }
815 }
816
817 static void initMoveData(WWindow * wwin, MoveData * data)
818 {
819         int i;
820         WWindow *tmp;
821
822         memset(data, 0, sizeof(MoveData));
823
824         for (i = 0, tmp = wwin->screen_ptr->focused_window; tmp != NULL; tmp = tmp->prev, i++) ;
825
826         if (i > 1) {
827                 data->topList = wmalloc(sizeof(WWindow *) * i);
828                 data->leftList = wmalloc(sizeof(WWindow *) * i);
829                 data->rightList = wmalloc(sizeof(WWindow *) * i);
830                 data->bottomList = wmalloc(sizeof(WWindow *) * i);
831
832                 updateMoveData(wwin, data);
833         }
834
835         data->realX = wwin->frame_x;
836         data->realY = wwin->frame_y;
837         data->calcX = wwin->frame_x;
838         data->calcY = wwin->frame_y;
839
840         data->winWidth = wwin->frame->core->width + (HAS_BORDER_WITH_SELECT(wwin) ? 2 * FRAME_BORDER_WIDTH : 0);
841         data->winHeight = wwin->frame->core->height + (HAS_BORDER_WITH_SELECT(wwin) ? 2 * FRAME_BORDER_WIDTH : 0);
842 }
843
844 static Bool checkWorkspaceChange(WWindow * wwin, MoveData * data, Bool opaqueMove)
845 {
846         WScreen *scr = wwin->screen_ptr;
847         Bool changed = False;
848
849         if (data->mouseX <= 1) {
850                 if (scr->current_workspace > 0) {
851
852                         crossWorkspace(scr, wwin, opaqueMove, scr->current_workspace - 1, True);
853                         changed = True;
854                         data->rubCount = 0;
855
856                 } else if (scr->current_workspace == 0 && wPreferences.ws_cycle) {
857
858                         crossWorkspace(scr, wwin, opaqueMove, scr->workspace_count - 1, True);
859                         changed = True;
860                         data->rubCount = 0;
861                 }
862         } else if (data->mouseX >= scr->scr_width - 2) {
863
864                 if (scr->current_workspace == scr->workspace_count - 1) {
865
866                         if (wPreferences.ws_cycle || scr->workspace_count == MAX_WORKSPACES) {
867
868                                 crossWorkspace(scr, wwin, opaqueMove, 0, False);
869                                 changed = True;
870                                 data->rubCount = 0;
871                         }
872                         /* if user insists on trying to go to next workspace even when
873                          * it's already the last, create a new one */
874                         else if (data->omouseX == data->mouseX && wPreferences.ws_advance) {
875
876                                 /* detect user "rubbing" the window against the edge */
877                                 if (data->rubCount > 0 && data->omouseY - data->mouseY > MOVE_THRESHOLD) {
878
879                                         data->rubCount = -(data->rubCount + 1);
880
881                                 } else if (data->rubCount <= 0 && data->mouseY - data->omouseY > MOVE_THRESHOLD) {
882
883                                         data->rubCount = -data->rubCount + 1;
884                                 }
885                         }
886                         /* create a new workspace */
887                         if (abs(data->rubCount) > 2) {
888                                 /* go to next workspace */
889                                 wWorkspaceNew(scr);
890
891                                 crossWorkspace(scr, wwin, opaqueMove, scr->current_workspace + 1, False);
892                                 changed = True;
893                                 data->rubCount = 0;
894                         }
895                 } else if (scr->current_workspace < scr->workspace_count) {
896
897                         /* go to next workspace */
898                         crossWorkspace(scr, wwin, opaqueMove, scr->current_workspace + 1, False);
899                         changed = True;
900                         data->rubCount = 0;
901                 }
902         } else {
903                 data->rubCount = 0;
904         }
905
906         return changed;
907 }
908
909 static void
910 updateWindowPosition(WWindow * wwin, MoveData * data, Bool doResistance,
911                      Bool opaqueMove, int newMouseX, int newMouseY)
912 {
913         WScreen *scr = wwin->screen_ptr;
914         int dx, dy;             /* how much mouse moved */
915         int winL, winR, winT, winB;     /* requested new window position */
916         int newX, newY;         /* actual new window position */
917         Bool hresist, vresist;
918         Bool updateIndex;
919         Bool attract;
920
921         hresist = False;
922         vresist = False;
923
924         updateIndex = False;
925
926         /* check the direction of the movement */
927         dx = newMouseX - data->mouseX;
928         dy = newMouseY - data->mouseY;
929
930         data->omouseX = data->mouseX;
931         data->omouseY = data->mouseY;
932         data->mouseX = newMouseX;
933         data->mouseY = newMouseY;
934
935         winL = data->calcX + dx;
936         winR = data->calcX + data->winWidth + dx;
937         winT = data->calcY + dy;
938         winB = data->calcY + data->winHeight + dy;
939
940         newX = data->realX;
941         newY = data->realY;
942
943         if (doResistance) {
944                 int l_edge, r_edge;
945                 int edge_l, edge_r;
946                 int t_edge, b_edge;
947                 int edge_t, edge_b;
948                 int resist;
949
950                 resist = WIN_RESISTANCE(wPreferences.edge_resistance);
951                 attract = wPreferences.attract;
952                 /* horizontal movement: check horizontal edge resistances */
953                 if (dx || dy) {
954                         WMRect rect;
955                         int i, head;
956                         /* window is the leftmost window: check against screen edge */
957
958                         /* Add inter head resistance 1/2 (if needed) */
959                         head = wGetHeadForPointerLocation(scr);
960                         rect = wGetRectForHead(scr, head);
961
962                         l_edge = WMAX(scr->totalUsableArea[head].x1, rect.pos.x);
963                         edge_l = l_edge - resist;
964                         edge_r = WMIN(scr->totalUsableArea[head].x2, rect.pos.x + rect.size.width);
965                         r_edge = edge_r + resist;
966
967                         /* 1 */
968                         if ((data->rightIndex >= 0) && (data->rightIndex <= data->count)) {
969                                 WWindow *looprw;
970
971                                 for (i = data->rightIndex - 1; i >= 0; i--) {
972                                         looprw = data->rightList[i];
973                                         if (!(data->realY > WBOTTOM(looprw)
974                                               || (data->realY + data->winHeight) < WTOP(looprw))) {
975                                                 if (attract || ((data->realX < (WRIGHT(looprw) + 2)) && dx < 0)) {
976                                                         l_edge = WRIGHT(looprw) + 1;
977                                                         resist = WIN_RESISTANCE(wPreferences.edge_resistance);
978                                                 }
979                                                 break;
980                                         }
981                                 }
982
983                                 if (attract) {
984                                         for (i = data->rightIndex; i < data->count; i++) {
985                                                 looprw = data->rightList[i];
986                                                 if (!(data->realY > WBOTTOM(looprw)
987                                                       || (data->realY + data->winHeight) < WTOP(looprw))) {
988                                                         r_edge = WRIGHT(looprw) + 1;
989                                                         resist = WIN_RESISTANCE(wPreferences.edge_resistance);
990                                                         break;
991                                                 }
992                                         }
993                                 }
994                         }
995
996                         if ((data->leftIndex >= 0) && (data->leftIndex <= data->count)) {
997                                 WWindow *looprw;
998
999                                 for (i = data->leftIndex - 1; i >= 0; i--) {
1000                                         looprw = data->leftList[i];
1001                                         if (!(data->realY > WBOTTOM(looprw)
1002                                               || (data->realY + data->winHeight) < WTOP(looprw))) {
1003                                                 if (attract
1004                                                     || (((data->realX + data->winWidth) > (WLEFT(looprw) - 1))
1005                                                         && dx > 0)) {
1006                                                         edge_r = WLEFT(looprw);
1007                                                         resist = WIN_RESISTANCE(wPreferences.edge_resistance);
1008                                                 }
1009                                                 break;
1010                                         }
1011                                 }
1012
1013                                 if (attract)
1014                                         for (i = data->leftIndex; i < data->count; i++) {
1015                                                 looprw = data->leftList[i];
1016                                                 if (!(data->realY > WBOTTOM(looprw)
1017                                                       || (data->realY + data->winHeight) < WTOP(looprw))) {
1018                                                         edge_l = WLEFT(looprw);
1019                                                         resist = WIN_RESISTANCE(wPreferences.edge_resistance);
1020                                                         break;
1021                                                 }
1022                                         }
1023                         }
1024
1025                         /*
1026                            printf("%d %d\n",winL,winR);
1027                            printf("l_ %d r_ %d _l %d _r %d\n",l_edge,r_edge,edge_l,edge_r);
1028                          */
1029
1030                         if ((winL - l_edge) < (r_edge - winL)) {
1031                                 if (resist > 0) {
1032                                         if ((attract && winL <= l_edge + resist && winL >= l_edge - resist)
1033                                             || (dx < 0 && winL <= l_edge && winL >= l_edge - resist)) {
1034                                                 newX = l_edge;
1035                                                 hresist = True;
1036                                         }
1037                                 }
1038                         } else {
1039                                 if (resist > 0 && attract && winL >= r_edge - resist && winL <= r_edge + resist) {
1040                                         newX = r_edge;
1041                                         hresist = True;
1042                                 }
1043                         }
1044
1045                         if ((winR - edge_l) < (edge_r - winR)) {
1046                                 if (resist > 0 && attract && winR <= edge_l + resist && winR >= edge_l - resist) {
1047                                         newX = edge_l - data->winWidth;
1048                                         hresist = True;
1049                                 }
1050                         } else {
1051                                 if (resist > 0) {
1052                                         if ((attract && winR >= edge_r - resist && winR <= edge_r + resist)
1053                                             || (dx > 0 && winR >= edge_r && winR <= edge_r + resist)) {
1054                                                 newX = edge_r - data->winWidth;
1055                                                 hresist = True;
1056                                         }
1057                                 }
1058                         }
1059
1060                         /* VeRT */
1061                         /* Add inter head resistance 2/2 (if needed) */
1062                         t_edge = WMAX(scr->totalUsableArea[head].y1, rect.pos.y);
1063                         edge_t = t_edge - resist;
1064                         edge_b = WMIN(scr->totalUsableArea[head].y2, rect.pos.y + rect.size.height);
1065                         b_edge = edge_b + resist;
1066
1067                         if ((data->bottomIndex >= 0) && (data->bottomIndex <= data->count)) {
1068                                 WWindow *looprw;
1069
1070                                 for (i = data->bottomIndex - 1; i >= 0; i--) {
1071                                         looprw = data->bottomList[i];
1072                                         if (!(data->realX > WRIGHT(looprw)
1073                                               || (data->realX + data->winWidth) < WLEFT(looprw))) {
1074                                                 if (attract || ((data->realY < (WBOTTOM(looprw) + 2)) && dy < 0)) {
1075                                                         t_edge = WBOTTOM(looprw) + 1;
1076                                                         resist = WIN_RESISTANCE(wPreferences.edge_resistance);
1077                                                 }
1078                                                 break;
1079                                         }
1080                                 }
1081
1082                                 if (attract) {
1083                                         for (i = data->bottomIndex; i < data->count; i++) {
1084                                                 looprw = data->bottomList[i];
1085                                                 if (!(data->realX > WRIGHT(looprw)
1086                                                       || (data->realX + data->winWidth) < WLEFT(looprw))) {
1087                                                         b_edge = WBOTTOM(looprw) + 1;
1088                                                         resist = WIN_RESISTANCE(wPreferences.edge_resistance);
1089                                                         break;
1090                                                 }
1091                                         }
1092                                 }
1093                         }
1094
1095                         if ((data->topIndex >= 0) && (data->topIndex <= data->count)) {
1096                                 WWindow *looprw;
1097
1098                                 for (i = data->topIndex - 1; i >= 0; i--) {
1099                                         looprw = data->topList[i];
1100                                         if (!(data->realX > WRIGHT(looprw)
1101                                               || (data->realX + data->winWidth) < WLEFT(looprw))) {
1102                                                 if (attract
1103                                                     || (((data->realY + data->winHeight) > (WTOP(looprw) - 1))
1104                                                         && dy > 0)) {
1105                                                         edge_b = WTOP(looprw);
1106                                                         resist = WIN_RESISTANCE(wPreferences.edge_resistance);
1107                                                 }
1108                                                 break;
1109                                         }
1110                                 }
1111
1112                                 if (attract)
1113                                         for (i = data->topIndex; i < data->count; i++) {
1114                                                 looprw = data->topList[i];
1115                                                 if (!(data->realX > WRIGHT(looprw)
1116                                                       || (data->realX + data->winWidth) < WLEFT(looprw))) {
1117                                                         edge_t = WTOP(looprw);
1118                                                         resist = WIN_RESISTANCE(wPreferences.edge_resistance);
1119                                                         break;
1120                                                 }
1121                                         }
1122                         }
1123
1124                         if ((winT - t_edge) < (b_edge - winT)) {
1125                                 if (resist > 0) {
1126                                         if ((attract && winT <= t_edge + resist && winT >= t_edge - resist)
1127                                             || (dy < 0 && winT <= t_edge && winT >= t_edge - resist)) {
1128                                                 newY = t_edge;
1129                                                 vresist = True;
1130                                         }
1131                                 }
1132                         } else {
1133                                 if (resist > 0 && attract && winT >= b_edge - resist && winT <= b_edge + resist) {
1134                                         newY = b_edge;
1135                                         vresist = True;
1136                                 }
1137                         }
1138
1139                         if ((winB - edge_t) < (edge_b - winB)) {
1140                                 if (resist > 0 && attract && winB <= edge_t + resist && winB >= edge_t - resist) {
1141                                         newY = edge_t - data->winHeight;
1142                                         vresist = True;
1143                                 }
1144                         } else {
1145                                 if (resist > 0) {
1146                                         if ((attract && winB >= edge_b - resist && winB <= edge_b + resist)
1147                                             || (dy > 0 && winB >= edge_b && winB <= edge_b + resist)) {
1148                                                 newY = edge_b - data->winHeight;
1149                                                 vresist = True;
1150                                         }
1151                                 }
1152                         }
1153                 }
1154                 /* END VeRT */
1155
1156         }
1157
1158         /* update window position */
1159         data->calcX += dx;
1160         data->calcY += dy;
1161
1162         if (((dx > 0 && data->calcX - data->realX > 0)
1163              || (dx < 0 && data->calcX - data->realX < 0)) && !hresist)
1164                 newX = data->calcX;
1165
1166         if (((dy > 0 && data->calcY - data->realY > 0)
1167              || (dy < 0 && data->calcY - data->realY < 0)) && !vresist)
1168                 newY = data->calcY;
1169
1170         if (data->realX != newX || data->realY != newY) {
1171
1172                 if (wPreferences.move_display == WDIS_NEW && !scr->selected_windows) {
1173                         showPosition(wwin, data->realX, data->realY);
1174                 }
1175                 if (opaqueMove) {
1176                         doWindowMove(wwin, scr->selected_windows, newX - wwin->frame_x, newY - wwin->frame_y);
1177                 } else {
1178                         /* erase frames */
1179                         drawFrames(wwin, scr->selected_windows,
1180                                    data->realX - wwin->frame_x, data->realY - wwin->frame_y);
1181                 }
1182
1183                 if (!scr->selected_windows && wPreferences.move_display == WDIS_FRAME_CENTER) {
1184
1185                         moveGeometryDisplayCentered(scr, newX + data->winWidth / 2, newY + data->winHeight / 2);
1186                 }
1187
1188                 if (!opaqueMove) {
1189                         /* draw frames */
1190                         drawFrames(wwin, scr->selected_windows, newX - wwin->frame_x, newY - wwin->frame_y);
1191                 }
1192
1193                 if (!scr->selected_windows) {
1194                         showPosition(wwin, newX, newY);
1195                 }
1196         }
1197
1198         /* recalc relative window position */
1199         if (doResistance && (data->realX != newX || data->realY != newY)) {
1200                 updateResistance(wwin, data, newX, newY);
1201         }
1202
1203         data->realX = newX;
1204         data->realY = newY;
1205 }
1206
1207 #define _KS KEY_CONTROL_WINDOW_WEIGHT
1208
1209 #define MOVABLE_BIT     0x01
1210 #define RESIZABLE_BIT   0x02
1211
1212 int wKeyboardMoveResizeWindow(WWindow * wwin)
1213 {
1214         WScreen *scr = wwin->screen_ptr;
1215         Window root = scr->root_win;
1216         XEvent event;
1217         int w = wwin->frame->core->width;
1218         int h = wwin->frame->core->height;
1219         int scr_width = wwin->screen_ptr->scr_width;
1220         int scr_height = wwin->screen_ptr->scr_height;
1221         int vert_border = wwin->frame->top_width + wwin->frame->bottom_width;
1222         int src_x = wwin->frame_x;
1223         int src_y = wwin->frame_y;
1224         int done, off_x, off_y, ww, wh;
1225         int kspeed = _KS;
1226         Time lastTime = 0;
1227         KeyCode shiftl, shiftr, ctrll, ctrlmode;
1228         KeySym keysym = NoSymbol;
1229         int moment = 0;
1230         int modes = ((IS_MOVABLE(wwin) ? MOVABLE_BIT : 0) | (IS_RESIZABLE(wwin) ? RESIZABLE_BIT : 0));
1231         int head = ((wPreferences.auto_arrange_icons && wXineramaHeads(scr) > 1)
1232                     ? wGetHeadForWindow(wwin)
1233                     : scr->xine_info.primary_head);
1234
1235         shiftl = XKeysymToKeycode(dpy, XK_Shift_L);
1236         shiftr = XKeysymToKeycode(dpy, XK_Shift_R);
1237         ctrll = XKeysymToKeycode(dpy, XK_Control_L);
1238         ctrlmode = done = off_x = off_y = 0;
1239
1240         if (modes == RESIZABLE_BIT) {
1241                 ctrlmode = 1;
1242         }
1243
1244         XSync(dpy, False);
1245         wusleep(10000);
1246         XGrabKeyboard(dpy, root, False, GrabModeAsync, GrabModeAsync, CurrentTime);
1247
1248         if (!wwin->flags.selected) {
1249                 wUnselectWindows(scr);
1250         }
1251         XGrabServer(dpy);
1252         XGrabPointer(dpy, scr->root_win, True, PointerMotionMask
1253                      | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
1254                      GrabModeAsync, None, wCursor[WCUR_DEFAULT], CurrentTime);
1255
1256         if (wwin->flags.shaded || scr->selected_windows) {
1257                 if (scr->selected_windows)
1258                         drawFrames(wwin, scr->selected_windows, off_x, off_y);
1259                 else
1260                         drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, w, h);
1261                 if (!scr->selected_windows)
1262                         mapPositionDisplay(wwin, src_x, src_y, w, h);
1263         } else {
1264                 drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, w, h);
1265         }
1266         ww = w;
1267         wh = h;
1268         while (1) {
1269                 /*
1270                    looper.ox=off_x;
1271                    looper.oy=off_y;
1272                  */
1273                 do {
1274                         WMMaskEvent(dpy, KeyPressMask | ButtonReleaseMask
1275                                     | ButtonPressMask | ExposureMask, &event);
1276                         if (event.type == Expose) {
1277                                 WMHandleEvent(&event);
1278                         }
1279                 } while (event.type == Expose);
1280
1281                 if (wwin->flags.shaded || scr->selected_windows) {
1282                         if (scr->selected_windows)
1283                                 drawFrames(wwin, scr->selected_windows, off_x, off_y);
1284                         else
1285                                 drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, w, h);
1286             /*** I HATE EDGE RESISTANCE - ]d ***/
1287                 } else {
1288                         drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, ww, wh);
1289                 }
1290
1291                 if (ctrlmode)
1292                         showGeometry(wwin, src_x + off_x, src_y + off_y, src_x + off_x + ww, src_y + off_y + wh,
1293                                      0);
1294
1295                 XUngrabServer(dpy);
1296                 XSync(dpy, False);
1297
1298                 switch (event.type) {
1299                 case KeyPress:
1300                         /* accelerate */
1301                         if (event.xkey.time - lastTime > 50) {
1302                                 kspeed /= (1 + (event.xkey.time - lastTime) / 100);
1303                         } else {
1304                                 if (kspeed < 20) {
1305                                         kspeed++;
1306                                 }
1307                         }
1308                         if (kspeed < _KS)
1309                                 kspeed = _KS;
1310                         lastTime = event.xkey.time;
1311                         if (modes == (MOVABLE_BIT | RESIZABLE_BIT)) {
1312                                 if ((event.xkey.state & ControlMask) && !wwin->flags.shaded) {
1313                                         ctrlmode = 1;
1314                                         wUnselectWindows(scr);
1315                                 } else {
1316                                         ctrlmode = 0;
1317                                 }
1318                         }
1319                         if (event.xkey.keycode == shiftl || event.xkey.keycode == shiftr) {
1320                                 if (ctrlmode)
1321                                         cycleGeometryDisplay(wwin, src_x + off_x, src_y + off_y, ww, wh, 0);
1322                                 else
1323                                         cyclePositionDisplay(wwin, src_x + off_x, src_y + off_y, ww, wh);
1324                         } else {
1325
1326                                 keysym = XLookupKeysym(&event.xkey, 0);
1327                                 switch (keysym) {
1328                                 case XK_Return:
1329                                         done = 2;
1330                                         break;
1331                                 case XK_Escape:
1332                                         done = 1;
1333                                         break;
1334                                 case XK_Up:
1335 #ifdef XK_KP_Up
1336                                 case XK_KP_Up:
1337 #endif
1338                                 case XK_k:
1339                                         if (ctrlmode) {
1340                                                 if (moment != UP)
1341                                                         h = wh;
1342                                                 h -= kspeed;
1343                                                 moment = UP;
1344                                                 if (h < 1)
1345                                                         h = 1;
1346                                         } else
1347                                                 off_y -= kspeed;
1348                                         break;
1349                                 case XK_Down:
1350 #ifdef XK_KP_Down
1351                                 case XK_KP_Down:
1352 #endif
1353                                 case XK_j:
1354                                         if (ctrlmode) {
1355                                                 if (moment != DOWN)
1356                                                         h = wh;
1357                                                 h += kspeed;
1358                                                 moment = DOWN;
1359                                         } else
1360                                                 off_y += kspeed;
1361                                         break;
1362                                 case XK_Left:
1363 #ifdef XK_KP_Left
1364                                 case XK_KP_Left:
1365 #endif
1366                                 case XK_h:
1367                                         if (ctrlmode) {
1368                                                 if (moment != LEFT)
1369                                                         w = ww;
1370                                                 w -= kspeed;
1371                                                 if (w < 1)
1372                                                         w = 1;
1373                                                 moment = LEFT;
1374                                         } else
1375                                                 off_x -= kspeed;
1376                                         break;
1377                                 case XK_Right:
1378 #ifdef XK_KP_Right
1379                                 case XK_KP_Right:
1380 #endif
1381                                 case XK_l:
1382                                         if (ctrlmode) {
1383                                                 if (moment != RIGHT)
1384                                                         w = ww;
1385                                                 w += kspeed;
1386                                                 moment = RIGHT;
1387                                         } else
1388                                                 off_x += kspeed;
1389                                         break;
1390                                 }
1391
1392                                 ww = w;
1393                                 wh = h;
1394                                 wh -= vert_border;
1395                                 wWindowConstrainSize(wwin, (unsigned int *)&ww, (unsigned int *)&wh);
1396                                 wh += vert_border;
1397
1398                                 if (wPreferences.ws_cycle) {
1399                                         if (src_x + off_x + ww < 20) {
1400                                                 if (!scr->current_workspace) {
1401                                                         wWorkspaceChange(scr, scr->workspace_count - 1);
1402                                                 } else
1403                                                         wWorkspaceChange(scr, scr->current_workspace - 1);
1404                                                 off_x += scr_width;
1405                                         } else if (src_x + off_x + 20 > scr_width) {
1406                                                 if (scr->current_workspace == scr->workspace_count - 1) {
1407                                                         wWorkspaceChange(scr, 0);
1408                                                 } else
1409                                                         wWorkspaceChange(scr, scr->current_workspace + 1);
1410                                                 off_x -= scr_width;
1411                                         }
1412                                 } else {
1413                                         if (src_x + off_x + ww < 20)
1414                                                 off_x = 20 - ww - src_x;
1415                                         else if (src_x + off_x + 20 > scr_width)
1416                                                 off_x = scr_width - 20 - src_x;
1417                                 }
1418
1419                                 if (src_y + off_y + wh < 20) {
1420                                         off_y = 20 - wh - src_y;
1421                                 } else if (src_y + off_y + 20 > scr_height) {
1422                                         off_y = scr_height - 20 - src_y;
1423                                 }
1424                         }
1425                         break;
1426                 case ButtonPress:
1427                 case ButtonRelease:
1428                         done = 1;
1429                         break;
1430                 case Expose:
1431                         WMHandleEvent(&event);
1432                         while (XCheckTypedEvent(dpy, Expose, &event)) {
1433                                 WMHandleEvent(&event);
1434                         }
1435                         break;
1436
1437                 default:
1438                         WMHandleEvent(&event);
1439                         break;
1440                 }
1441
1442                 XGrabServer(dpy);
1443                 /*xxx */
1444
1445                 if (wwin->flags.shaded && !scr->selected_windows) {
1446                         moveGeometryDisplayCentered(scr, src_x + off_x + w / 2, src_y + off_y + h / 2);
1447                 } else {
1448                         if (ctrlmode) {
1449                                 WMUnmapWidget(scr->gview);
1450                                 mapGeometryDisplay(wwin, src_x + off_x, src_y + off_y, ww, wh);
1451                         } else if (!scr->selected_windows) {
1452                                 WMUnmapWidget(scr->gview);
1453                                 mapPositionDisplay(wwin, src_x + off_x, src_y + off_y, ww, wh);
1454                         }
1455                 }
1456
1457                 if (wwin->flags.shaded || scr->selected_windows) {
1458                         if (scr->selected_windows)
1459                                 drawFrames(wwin, scr->selected_windows, off_x, off_y);
1460                         else
1461                                 drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, w, h);
1462                 } else {
1463                         drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, ww, wh);
1464                 }
1465
1466                 if (ctrlmode) {
1467                         showGeometry(wwin, src_x + off_x, src_y + off_y, src_x + off_x + ww, src_y + off_y + wh,
1468                                      0);
1469                 } else if (!scr->selected_windows)
1470                         showPosition(wwin, src_x + off_x, src_y + off_y);
1471
1472                 if (done) {
1473                         scr->keymove_tick = 0;
1474                         /*
1475                            WMDeleteTimerWithClientData(&looper);
1476                          */
1477                         if (wwin->flags.shaded || scr->selected_windows) {
1478                                 if (scr->selected_windows)
1479                                         drawFrames(wwin, scr->selected_windows, off_x, off_y);
1480                                 else
1481                                         drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, w, h);
1482                         } else {
1483                                 drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, ww, wh);
1484                         }
1485
1486                         if (ctrlmode) {
1487                                 showGeometry(wwin, src_x + off_x, src_y + off_y, src_x + off_x + ww,
1488                                              src_y + off_y + wh, 0);
1489                                 WMUnmapWidget(scr->gview);
1490                         } else
1491                                 WMUnmapWidget(scr->gview);
1492
1493                         XUngrabKeyboard(dpy, CurrentTime);
1494                         XUngrabPointer(dpy, CurrentTime);
1495                         XUngrabServer(dpy);
1496
1497                         if (done == 2) {
1498                                 if (wwin->flags.shaded || scr->selected_windows) {
1499                                         if (!scr->selected_windows) {
1500                                                 wWindowMove(wwin, src_x + off_x, src_y + off_y);
1501                                                 wWindowSynthConfigureNotify(wwin);
1502                                         } else {
1503                                                 WMArrayIterator iter;
1504                                                 WWindow *foo;
1505
1506                                                 doWindowMove(wwin, scr->selected_windows, off_x, off_y);
1507
1508                                                 WM_ITERATE_ARRAY(scr->selected_windows, foo, iter) {
1509                                                         wWindowSynthConfigureNotify(foo);
1510                                                 }
1511                                         }
1512                                 } else {
1513                                         if (wwin->client.width != ww)
1514                                                 wwin->flags.user_changed_width = 1;
1515
1516                                         if (wwin->client.height != wh - vert_border)
1517                                                 wwin->flags.user_changed_height = 1;
1518
1519                                         wWindowConfigure(wwin, src_x + off_x, src_y + off_y, ww, wh - vert_border);
1520                                         wWindowSynthConfigureNotify(wwin);
1521                                 }
1522                                 wWindowChangeWorkspace(wwin, scr->current_workspace);
1523                                 wSetFocusTo(scr, wwin);
1524                         }
1525
1526                         if (wPreferences.auto_arrange_icons && wXineramaHeads(scr) > 1 &&
1527                             head != wGetHeadForWindow(wwin)) {
1528                                 wArrangeIcons(scr, True);
1529                         }
1530
1531 #if defined(NETWM_HINTS) && defined(VIRTUAL_DESKTOP)
1532                         wWorkspaceResizeViewport(scr, scr->current_workspace);
1533 #endif
1534
1535                         return 1;
1536                 }
1537         }
1538 }
1539
1540 /*
1541  *----------------------------------------------------------------------
1542  * wMouseMoveWindow--
1543  *      Move the named window and the other selected ones (if any),
1544  * interactively. Also shows the position of the window, if only one
1545  * window is being moved.
1546  *      If the window is not on the selected window list, the selected
1547  * windows are deselected.
1548  *      If shift is pressed during the operation, the position display
1549  * is changed to another type.
1550  *
1551  * Returns:
1552  *      True if the window was moved, False otherwise.
1553  *
1554  * Side effects:
1555  *      The window(s) position is changed, and the client(s) are
1556  * notified about that.
1557  *      The position display configuration may be changed.
1558  *----------------------------------------------------------------------
1559  */
1560 int wMouseMoveWindow(WWindow * wwin, XEvent * ev)
1561 {
1562         WScreen *scr = wwin->screen_ptr;
1563         XEvent event;
1564         Window root = scr->root_win;
1565         KeyCode shiftl, shiftr;
1566         Bool done = False;
1567         int started = 0;
1568         int warped = 0;
1569         /* This needs not to change while moving, else bad things can happen */
1570         int opaqueMove = wPreferences.opaque_move;
1571         MoveData moveData;
1572         int head = ((wPreferences.auto_arrange_icons && wXineramaHeads(scr) > 1)
1573                     ? wGetHeadForWindow(wwin)
1574                     : scr->xine_info.primary_head);
1575 #ifdef GHOST_WINDOW_MOVE
1576         RImage *rimg = InitGhostWindowMove(scr);
1577 #endif
1578
1579         if (!IS_MOVABLE(wwin))
1580                 return False;
1581
1582         if (wPreferences.opaque_move && !wPreferences.use_saveunders) {
1583                 XSetWindowAttributes attr;
1584
1585                 attr.save_under = True;
1586                 XChangeWindowAttributes(dpy, wwin->frame->core->window, CWSaveUnder, &attr);
1587         }
1588
1589         initMoveData(wwin, &moveData);
1590
1591         moveData.mouseX = ev->xmotion.x_root;
1592         moveData.mouseY = ev->xmotion.y_root;
1593
1594         if (!wwin->flags.selected) {
1595                 /* this window is not selected, unselect others and move only wwin */
1596                 wUnselectWindows(scr);
1597         }
1598 #ifdef DEBUG
1599         puts("Moving window");
1600 #endif
1601         shiftl = XKeysymToKeycode(dpy, XK_Shift_L);
1602         shiftr = XKeysymToKeycode(dpy, XK_Shift_R);
1603         while (!done) {
1604                 if (warped) {
1605                         int junk;
1606                         Window junkw;
1607
1608                         /* XWarpPointer() doesn't seem to generate Motion events, so
1609                          * we've got to simulate them */
1610                         XQueryPointer(dpy, root, &junkw, &junkw, &event.xmotion.x_root,
1611                                       &event.xmotion.y_root, &junk, &junk, (unsigned *)&junk);
1612                 } else {
1613                         WMMaskEvent(dpy, KeyPressMask | ButtonMotionMask
1614                                     | PointerMotionHintMask
1615                                     | ButtonReleaseMask | ButtonPressMask | ExposureMask, &event);
1616
1617                         if (event.type == MotionNotify) {
1618                                 /* compress MotionNotify events */
1619                                 while (XCheckMaskEvent(dpy, ButtonMotionMask, &event)) ;
1620                                 if (!checkMouseSamplingRate(&event))
1621                                         continue;
1622                         }
1623                 }
1624                 switch (event.type) {
1625                 case KeyPress:
1626                         if ((event.xkey.keycode == shiftl || event.xkey.keycode == shiftr)
1627                             && started && !scr->selected_windows) {
1628
1629                                 if (!opaqueMove) {
1630                                         drawFrames(wwin, scr->selected_windows,
1631                                                    moveData.realX - wwin->frame_x, moveData.realY - wwin->frame_y);
1632                                 }
1633
1634                                 if (wPreferences.move_display == WDIS_NEW && !scr->selected_windows) {
1635                                         showPosition(wwin, moveData.realX, moveData.realY);
1636                                         XUngrabServer(dpy);
1637                                 }
1638                                 cyclePositionDisplay(wwin, moveData.realX, moveData.realY,
1639                                                      moveData.winWidth, moveData.winHeight);
1640
1641                                 if (wPreferences.move_display == WDIS_NEW && !scr->selected_windows) {
1642                                         XGrabServer(dpy);
1643                                         showPosition(wwin, moveData.realX, moveData.realY);
1644                                 }
1645
1646                                 if (!opaqueMove) {
1647                                         drawFrames(wwin, scr->selected_windows,
1648                                                    moveData.realX - wwin->frame_x, moveData.realY - wwin->frame_y);
1649                                 }
1650                                 /*} else {
1651                                    WMHandleEvent(&event); this causes problems needs fixing */
1652                         }
1653                         break;
1654
1655                 case MotionNotify:
1656                         if (started) {
1657                                 updateWindowPosition(wwin, &moveData,
1658                                                      scr->selected_windows == NULL
1659                                                      && wPreferences.edge_resistance > 0,
1660                                                      opaqueMove, event.xmotion.x_root, event.xmotion.y_root);
1661
1662                                 if (!warped && !wPreferences.no_autowrap) {
1663                                         int oldWorkspace = scr->current_workspace;
1664
1665                                         if (wPreferences.move_display == WDIS_NEW && !scr->selected_windows) {
1666                                                 showPosition(wwin, moveData.realX, moveData.realY);
1667                                                 XUngrabServer(dpy);
1668                                         }
1669                                         if (!opaqueMove) {
1670                                                 drawFrames(wwin, scr->selected_windows,
1671                                                            moveData.realX - wwin->frame_x,
1672                                                            moveData.realY - wwin->frame_y);
1673                                         }
1674                                         if (checkWorkspaceChange(wwin, &moveData, opaqueMove)) {
1675                                                 if (scr->current_workspace != oldWorkspace
1676                                                     && wPreferences.edge_resistance > 0
1677                                                     && scr->selected_windows == NULL)
1678                                                         updateMoveData(wwin, &moveData);
1679                                                 warped = 1;
1680                                         }
1681                                         if (!opaqueMove) {
1682                                                 drawFrames(wwin, scr->selected_windows,
1683                                                            moveData.realX - wwin->frame_x,
1684                                                            moveData.realY - wwin->frame_y);
1685                                         }
1686                                         if (wPreferences.move_display == WDIS_NEW && !scr->selected_windows) {
1687                                                 XSync(dpy, False);
1688                                                 showPosition(wwin, moveData.realX, moveData.realY);
1689                                                 XGrabServer(dpy);
1690                                         }
1691                                 } else {
1692                                         warped = 0;
1693                                 }
1694                         } else if (abs(ev->xmotion.x_root - event.xmotion.x_root) >= MOVE_THRESHOLD
1695                                    || abs(ev->xmotion.y_root - event.xmotion.y_root) >= MOVE_THRESHOLD) {
1696
1697                                 XChangeActivePointerGrab(dpy, ButtonMotionMask
1698                                                          | ButtonReleaseMask | ButtonPressMask,
1699                                                          wCursor[WCUR_MOVE], CurrentTime);
1700                                 started = 1;
1701                                 XGrabKeyboard(dpy, root, False, GrabModeAsync, GrabModeAsync, CurrentTime);
1702
1703                                 if (!scr->selected_windows)
1704                                         mapPositionDisplay(wwin, moveData.realX, moveData.realY,
1705                                                            moveData.winWidth, moveData.winHeight);
1706
1707                                 if (started && !opaqueMove)
1708                                         drawFrames(wwin, scr->selected_windows, 0, 0);
1709
1710                                 if (!opaqueMove || (wPreferences.move_display == WDIS_NEW
1711                                                     && !scr->selected_windows)) {
1712                                         XGrabServer(dpy);
1713                                         if (wPreferences.move_display == WDIS_NEW)
1714                                                 showPosition(wwin, moveData.realX, moveData.realY);
1715                                 }
1716                         }
1717                         break;
1718
1719                 case ButtonPress:
1720                         break;
1721
1722                 case ButtonRelease:
1723                         if (event.xbutton.button != ev->xbutton.button)
1724                                 break;
1725
1726                         if (started) {
1727                                 XEvent e;
1728                                 if (!opaqueMove) {
1729                                         drawFrames(wwin, scr->selected_windows,
1730                                                    moveData.realX - wwin->frame_x, moveData.realY - wwin->frame_y);
1731                                         XSync(dpy, 0);
1732                                         doWindowMove(wwin, scr->selected_windows,
1733                                                      moveData.realX - wwin->frame_x,
1734                                                      moveData.realY - wwin->frame_y);
1735                                 }
1736 #ifndef CONFIGURE_WINDOW_WHILE_MOVING
1737                                 wWindowSynthConfigureNotify(wwin);
1738 #endif
1739                                 XUngrabKeyboard(dpy, CurrentTime);
1740                                 XUngrabServer(dpy);
1741                                 if (!opaqueMove) {
1742                                         wWindowChangeWorkspace(wwin, scr->current_workspace);
1743                                         wSetFocusTo(scr, wwin);
1744                                 }
1745                                 if (wPreferences.move_display == WDIS_NEW)
1746                                         showPosition(wwin, moveData.realX, moveData.realY);
1747
1748                                 /* discard all enter/leave events that happened until
1749                                  * the time the button was released */
1750                                 while (XCheckTypedEvent(dpy, EnterNotify, &e)) {
1751                                         if (e.xcrossing.time > event.xbutton.time) {
1752                                                 XPutBackEvent(dpy, &e);
1753                                                 break;
1754                                         }
1755                                 }
1756                                 while (XCheckTypedEvent(dpy, LeaveNotify, &e)) {
1757                                         if (e.xcrossing.time > event.xbutton.time) {
1758                                                 XPutBackEvent(dpy, &e);
1759                                                 break;
1760                                         }
1761                                 }
1762
1763                                 if (!scr->selected_windows) {
1764                                         /* get rid of the geometry window */
1765                                         WMUnmapWidget(scr->gview);
1766                                 }
1767                         }
1768 #ifdef DEBUG
1769                         puts("End move window");
1770 #endif
1771                         done = True;
1772                         break;
1773
1774                 default:
1775                         if (started && !opaqueMove) {
1776                                 drawFrames(wwin, scr->selected_windows,
1777                                            moveData.realX - wwin->frame_x, moveData.realY - wwin->frame_y);
1778                                 XUngrabServer(dpy);
1779                                 WMHandleEvent(&event);
1780                                 XSync(dpy, False);
1781                                 XGrabServer(dpy);
1782                                 drawFrames(wwin, scr->selected_windows,
1783                                            moveData.realX - wwin->frame_x, moveData.realY - wwin->frame_y);
1784                         } else {
1785                                 WMHandleEvent(&event);
1786                         }
1787                         break;
1788                 }
1789         }
1790
1791         if (wPreferences.opaque_move && !wPreferences.use_saveunders) {
1792                 XSetWindowAttributes attr;
1793
1794                 attr.save_under = False;
1795                 XChangeWindowAttributes(dpy, wwin->frame->core->window, CWSaveUnder, &attr);
1796
1797         }
1798
1799         freeMoveData(&moveData);
1800
1801         if (started && wPreferences.auto_arrange_icons && wXineramaHeads(scr) > 1 &&
1802             head != wGetHeadForWindow(wwin)) {
1803                 wArrangeIcons(scr, True);
1804         }
1805 #if defined(NETWM_HINTS) && defined(VIRTUAL_DESKTOP)
1806         if (started)
1807                 wWorkspaceResizeViewport(scr, scr->current_workspace);
1808 #endif
1809
1810         return started;
1811 }
1812
1813 #define RESIZEBAR       1
1814 #define HCONSTRAIN      2
1815
1816 static int getResizeDirection(WWindow * wwin, int x, int y, int dx, int dy, int flags)
1817 {
1818         int w = wwin->frame->core->width - 1;
1819         int cw = wwin->frame->resizebar_corner_width;
1820         int dir;
1821
1822         /* if not resizing through the resizebar */
1823         if (!(flags & RESIZEBAR)) {
1824                 int xdir = (abs(x) < (wwin->client.width / 2)) ? LEFT : RIGHT;
1825                 int ydir = (abs(y) < (wwin->client.height / 2)) ? UP : DOWN;
1826                 if (abs(dx) < 2 || abs(dy) < 2) {
1827                         if (abs(dy) > abs(dx))
1828                                 xdir = 0;
1829                         else
1830                                 ydir = 0;
1831                 }
1832                 return (xdir | ydir);
1833         }
1834
1835         /* window is too narrow. allow diagonal resize */
1836         if (cw * 2 >= w) {
1837                 int ydir;
1838
1839                 if (flags & HCONSTRAIN)
1840                         ydir = 0;
1841                 else
1842                         ydir = DOWN;
1843                 if (x < cw)
1844                         return (LEFT | ydir);
1845                 else
1846                         return (RIGHT | ydir);
1847         }
1848         /* vertical resize */
1849         if ((x > cw) && (x < w - cw))
1850                 return DOWN;
1851
1852         if (x < cw)
1853                 dir = LEFT;
1854         else
1855                 dir = RIGHT;
1856
1857         if ((abs(dy) > 0) && !(flags & HCONSTRAIN))
1858                 dir |= DOWN;
1859
1860         return dir;
1861 }
1862
1863 void wMouseResizeWindow(WWindow * wwin, XEvent * ev)
1864 {
1865         XEvent event;
1866         WScreen *scr = wwin->screen_ptr;
1867         Window root = scr->root_win;
1868         int vert_border = wwin->frame->top_width + wwin->frame->bottom_width;
1869         int fw = wwin->frame->core->width;
1870         int fh = wwin->frame->core->height;
1871         int fx = wwin->frame_x;
1872         int fy = wwin->frame_y;
1873         int is_resizebar = (wwin->frame->resizebar && ev->xany.window == wwin->frame->resizebar->window);
1874         int orig_x, orig_y;
1875         int started;
1876         int dw, dh;
1877         int rw = fw, rh = fh;
1878         int rx1, ry1, rx2, ry2;
1879         int res = 0;
1880         KeyCode shiftl, shiftr;
1881         int h = 0;
1882         int orig_fx = fx;
1883         int orig_fy = fy;
1884         int orig_fw = fw;
1885         int orig_fh = fh;
1886         int head = ((wPreferences.auto_arrange_icons && wXineramaHeads(scr) > 1)
1887                     ? wGetHeadForWindow(wwin)
1888                     : scr->xine_info.primary_head);
1889
1890         if (!IS_RESIZABLE(wwin))
1891                 return;
1892
1893         if (wwin->flags.shaded) {
1894                 wwarning("internal error: tryein");
1895                 return;
1896         }
1897         orig_x = ev->xbutton.x_root;
1898         orig_y = ev->xbutton.y_root;
1899
1900         started = 0;
1901 #ifdef DEBUG
1902         puts("Resizing window");
1903 #endif
1904
1905         wUnselectWindows(scr);
1906         rx1 = fx;
1907         rx2 = fx + fw - 1;
1908         ry1 = fy;
1909         ry2 = fy + fh - 1;
1910         shiftl = XKeysymToKeycode(dpy, XK_Shift_L);
1911         shiftr = XKeysymToKeycode(dpy, XK_Shift_R);
1912         if (HAS_TITLEBAR(wwin))
1913                 h = WMFontHeight(wwin->screen_ptr->title_font) + (wPreferences.window_title_clearance +
1914                                                                   TITLEBAR_EXTEND_SPACE) * 2;
1915         else
1916                 h = 0;
1917         while (1) {
1918                 WMMaskEvent(dpy, KeyPressMask | ButtonMotionMask
1919                             | ButtonReleaseMask | PointerMotionHintMask | ButtonPressMask | ExposureMask, &event);
1920                 if (!checkMouseSamplingRate(&event))
1921                         continue;
1922
1923                 switch (event.type) {
1924                 case KeyPress:
1925                         showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
1926                         if ((event.xkey.keycode == shiftl || event.xkey.keycode == shiftr)
1927                             && started) {
1928                                 drawTransparentFrame(wwin, fx, fy, fw, fh);
1929                                 cycleGeometryDisplay(wwin, fx, fy, fw, fh, res);
1930                                 drawTransparentFrame(wwin, fx, fy, fw, fh);
1931                         }
1932                         showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
1933                         break;
1934
1935                 case MotionNotify:
1936                         if (started) {
1937                                 while (XCheckMaskEvent(dpy, ButtonMotionMask, &event)) ;
1938
1939                                 dw = 0;
1940                                 dh = 0;
1941
1942                                 orig_fx = fx;
1943                                 orig_fy = fy;
1944                                 orig_fw = fw;
1945                                 orig_fh = fh;
1946
1947                                 if (res & LEFT)
1948                                         dw = orig_x - event.xmotion.x_root;
1949                                 else if (res & RIGHT)
1950                                         dw = event.xmotion.x_root - orig_x;
1951                                 if (res & UP)
1952                                         dh = orig_y - event.xmotion.y_root;
1953                                 else if (res & DOWN)
1954                                         dh = event.xmotion.y_root - orig_y;
1955
1956                                 orig_x = event.xmotion.x_root;
1957                                 orig_y = event.xmotion.y_root;
1958
1959                                 rw += dw;
1960                                 rh += dh;
1961                                 fw = rw;
1962                                 fh = rh - vert_border;
1963                                 wWindowConstrainSize(wwin, (unsigned int *)&fw, (unsigned int *)&fh);
1964                                 fh += vert_border;
1965                                 if (res & LEFT)
1966                                         fx = rx2 - fw + 1;
1967                                 else if (res & RIGHT)
1968                                         fx = rx1;
1969                                 if (res & UP)
1970                                         fy = ry2 - fh + 1;
1971                                 else if (res & DOWN)
1972                                         fy = ry1;
1973                         } else if (abs(orig_x - event.xmotion.x_root) >= MOVE_THRESHOLD
1974                                    || abs(orig_y - event.xmotion.y_root) >= MOVE_THRESHOLD) {
1975                                 int tx, ty;
1976                                 Window junkw;
1977                                 int flags;
1978
1979                                 XTranslateCoordinates(dpy, root, wwin->frame->core->window,
1980                                                       orig_x, orig_y, &tx, &ty, &junkw);
1981
1982                                 /* check if resizing through resizebar */
1983                                 if (is_resizebar)
1984                                         flags = RESIZEBAR;
1985                                 else
1986                                         flags = 0;
1987
1988                                 if (is_resizebar && ((ev->xbutton.state & ShiftMask)
1989                                                      || abs(orig_y - event.xmotion.y_root) < HRESIZE_THRESHOLD))
1990                                         flags |= HCONSTRAIN;
1991
1992                                 res = getResizeDirection(wwin, tx, ty,
1993                                                          orig_x - event.xmotion.x_root,
1994                                                          orig_y - event.xmotion.y_root, flags);
1995
1996                                 if (res == (UP | LEFT))
1997                                         XChangeActivePointerGrab(dpy, ButtonMotionMask
1998                                                                  | ButtonReleaseMask | ButtonPressMask,
1999                                                                  wCursor[WCUR_TOPLEFTRESIZE], CurrentTime);
2000                                 else if (res == (UP | RIGHT))
2001                                         XChangeActivePointerGrab(dpy, ButtonMotionMask
2002                                                                  | ButtonReleaseMask | ButtonPressMask,
2003                                                                  wCursor[WCUR_TOPRIGHTRESIZE], CurrentTime);
2004                                 else if (res == (DOWN | LEFT))
2005                                         XChangeActivePointerGrab(dpy, ButtonMotionMask
2006                                                                  | ButtonReleaseMask | ButtonPressMask,
2007                                                                  wCursor[WCUR_BOTTOMLEFTRESIZE], CurrentTime);
2008                                 else if (res == (DOWN | RIGHT))
2009                                         XChangeActivePointerGrab(dpy, ButtonMotionMask
2010                                                                  | ButtonReleaseMask | ButtonPressMask,
2011                                                                  wCursor[WCUR_BOTTOMRIGHTRESIZE], CurrentTime);
2012                                 else if (res == DOWN || res == UP)
2013                                         XChangeActivePointerGrab(dpy, ButtonMotionMask
2014                                                                  | ButtonReleaseMask | ButtonPressMask,
2015                                                                  wCursor[WCUR_VERTICALRESIZE], CurrentTime);
2016                                 else if (res & (DOWN | UP))
2017                                         XChangeActivePointerGrab(dpy, ButtonMotionMask
2018                                                                  | ButtonReleaseMask | ButtonPressMask,
2019                                                                  wCursor[WCUR_VERTICALRESIZE], CurrentTime);
2020                                 else if (res & (LEFT | RIGHT))
2021                                         XChangeActivePointerGrab(dpy, ButtonMotionMask
2022                                                                  | ButtonReleaseMask | ButtonPressMask,
2023                                                                  wCursor[WCUR_HORIZONRESIZE], CurrentTime);
2024
2025                                 XGrabKeyboard(dpy, root, False, GrabModeAsync, GrabModeAsync, CurrentTime);
2026
2027                                 XGrabServer(dpy);
2028
2029                                 /* Draw the resize frame for the first time. */
2030                                 mapGeometryDisplay(wwin, fx, fy, fw, fh);
2031
2032                                 drawTransparentFrame(wwin, fx, fy, fw, fh);
2033
2034                                 showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
2035
2036                                 started = 1;
2037                         }
2038                         if (started) {
2039                                 if (wPreferences.size_display == WDIS_FRAME_CENTER) {
2040                                         drawTransparentFrame(wwin, orig_fx, orig_fy, orig_fw, orig_fh);
2041                                         moveGeometryDisplayCentered(scr, fx + fw / 2, fy + fh / 2);
2042                                         drawTransparentFrame(wwin, fx, fy, fw, fh);
2043                                 } else {
2044                                         drawTransparentFrame(wwin, orig_fx, orig_fy, orig_fw, orig_fh);
2045                                         drawTransparentFrame(wwin, fx, fy, fw, fh);
2046                                 }
2047                                 if (fh != orig_fh || fw != orig_fw) {
2048                                         if (wPreferences.size_display == WDIS_NEW) {
2049                                                 showGeometry(wwin, orig_fx, orig_fy, orig_fx + orig_fw,
2050                                                              orig_fy + orig_fh, res);
2051                                         }
2052                                         showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
2053                                 }
2054                         }
2055                         break;
2056
2057                 case ButtonPress:
2058                         break;
2059
2060                 case ButtonRelease:
2061                         if (event.xbutton.button != ev->xbutton.button)
2062                                 break;
2063
2064                         if (started) {
2065                                 showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
2066
2067                                 drawTransparentFrame(wwin, fx, fy, fw, fh);
2068
2069                                 XUngrabKeyboard(dpy, CurrentTime);
2070                                 WMUnmapWidget(scr->gview);
2071                                 XUngrabServer(dpy);
2072
2073                                 if (wwin->client.width != fw)
2074                                         wwin->flags.user_changed_width = 1;
2075
2076                                 if (wwin->client.height != fh - vert_border)
2077                                         wwin->flags.user_changed_height = 1;
2078
2079                                 wWindowConfigure(wwin, fx, fy, fw, fh - vert_border);
2080                         }
2081 #ifdef DEBUG
2082                         puts("End resize window");
2083 #endif
2084                         return;
2085
2086                 default:
2087                         WMHandleEvent(&event);
2088                 }
2089         }
2090
2091         if (wPreferences.auto_arrange_icons && wXineramaHeads(scr) > 1 && head != wGetHeadForWindow(wwin)) {
2092                 wArrangeIcons(scr, True);
2093         }
2094 #if defined(NETWM_HINTS) && defined(VIRTUAL_DESKTOP)
2095         wWorkspaceResizeViewport(scr, scr->current_workspace);
2096 #endif
2097 }
2098
2099 #undef LEFT
2100 #undef RIGHT
2101 #undef HORIZONTAL
2102 #undef UP
2103 #undef DOWN
2104 #undef VERTICAL
2105 #undef HCONSTRAIN
2106 #undef RESIZEBAR
2107
2108 void wUnselectWindows(WScreen * scr)
2109 {
2110         WWindow *wwin;
2111
2112         if (!scr->selected_windows)
2113                 return;
2114
2115         while (WMGetArrayItemCount(scr->selected_windows)) {
2116                 wwin = WMGetFromArray(scr->selected_windows, 0);
2117                 if (wwin->flags.miniaturized && wwin->icon && wwin->icon->selected)
2118                         wIconSelect(wwin->icon);
2119
2120                 wSelectWindow(wwin, False);
2121         }
2122         WMFreeArray(scr->selected_windows);
2123         scr->selected_windows = NULL;
2124 }
2125
2126 #ifndef LITE
2127 static void selectWindowsInside(WScreen * scr, int x1, int y1, int x2, int y2)
2128 {
2129         WWindow *tmpw;
2130
2131         /* select the windows and put them in the selected window list */
2132         tmpw = scr->focused_window;
2133         while (tmpw != NULL) {
2134                 if (!(tmpw->flags.miniaturized || tmpw->flags.hidden)) {
2135                         if ((tmpw->frame->workspace == scr->current_workspace || IS_OMNIPRESENT(tmpw))
2136                             && (tmpw->frame_x >= x1) && (tmpw->frame_y >= y1)
2137                             && (tmpw->frame->core->width + tmpw->frame_x <= x2)
2138                             && (tmpw->frame->core->height + tmpw->frame_y <= y2)) {
2139                                 wSelectWindow(tmpw, True);
2140                         }
2141                 }
2142                 tmpw = tmpw->prev;
2143         }
2144 }
2145
2146 void wSelectWindows(WScreen * scr, XEvent * ev)
2147 {
2148         XEvent event;
2149         Window root = scr->root_win;
2150         GC gc = scr->frame_gc;
2151         int xp = ev->xbutton.x_root;
2152         int yp = ev->xbutton.y_root;
2153         int w = 0, h = 0;
2154         int x = xp, y = yp;
2155
2156 #ifdef DEBUG
2157         puts("Selecting windows");
2158 #endif
2159         if (XGrabPointer(dpy, scr->root_win, False, ButtonMotionMask
2160                          | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
2161                          GrabModeAsync, None, wCursor[WCUR_DEFAULT], CurrentTime) != Success) {
2162                 return;
2163         }
2164         XGrabServer(dpy);
2165
2166         wUnselectWindows(scr);
2167
2168         XDrawRectangle(dpy, root, gc, xp, yp, w, h);
2169         while (1) {
2170                 WMMaskEvent(dpy, ButtonReleaseMask | PointerMotionMask | ButtonPressMask, &event);
2171
2172                 switch (event.type) {
2173                 case MotionNotify:
2174                         XDrawRectangle(dpy, root, gc, x, y, w, h);
2175                         x = event.xmotion.x_root;
2176                         if (x < xp) {
2177                                 w = xp - x;
2178                         } else {
2179                                 w = x - xp;
2180                                 x = xp;
2181                         }
2182                         y = event.xmotion.y_root;
2183                         if (y < yp) {
2184                                 h = yp - y;
2185                         } else {
2186                                 h = y - yp;
2187                                 y = yp;
2188                         }
2189                         XDrawRectangle(dpy, root, gc, x, y, w, h);
2190                         break;
2191
2192                 case ButtonPress:
2193                         break;
2194
2195                 case ButtonRelease:
2196                         if (event.xbutton.button != ev->xbutton.button)
2197                                 break;
2198
2199                         XDrawRectangle(dpy, root, gc, x, y, w, h);
2200                         XUngrabServer(dpy);
2201                         XUngrabPointer(dpy, CurrentTime);
2202                         selectWindowsInside(scr, x, y, x + w, y + h);
2203
2204 #ifdef DEBUG
2205                         puts("End window selection");
2206 #endif
2207                         return;
2208
2209                 default:
2210                         WMHandleEvent(&event);
2211                         break;
2212                 }
2213         }
2214 }
2215 #endif                          /* !LITE */
2216
2217 void InteractivePlaceWindow(WWindow * wwin, int *x_ret, int *y_ret, unsigned width, unsigned height)
2218 {
2219         WScreen *scr = wwin->screen_ptr;
2220         Window root = scr->root_win;
2221         int x, y, h = 0;
2222         XEvent event;
2223         KeyCode shiftl, shiftr;
2224         Window junkw;
2225         int junk;
2226
2227         if (XGrabPointer(dpy, root, True, PointerMotionMask | ButtonPressMask,
2228                          GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_DEFAULT], CurrentTime) != Success) {
2229                 *x_ret = 0;
2230                 *y_ret = 0;
2231                 return;
2232         }
2233         if (HAS_TITLEBAR(wwin)) {
2234                 h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance +
2235                                                      TITLEBAR_EXTEND_SPACE) * 2;
2236                 height += h;
2237         }
2238         if (HAS_RESIZEBAR(wwin)) {
2239                 height += RESIZEBAR_HEIGHT;
2240         }
2241         XGrabKeyboard(dpy, root, False, GrabModeAsync, GrabModeAsync, CurrentTime);
2242         XQueryPointer(dpy, root, &junkw, &junkw, &x, &y, &junk, &junk, (unsigned *)&junk);
2243         mapPositionDisplay(wwin, x - width / 2, y - h / 2, width, height);
2244
2245         drawTransparentFrame(wwin, x - width / 2, y - h / 2, width, height);
2246
2247         shiftl = XKeysymToKeycode(dpy, XK_Shift_L);
2248         shiftr = XKeysymToKeycode(dpy, XK_Shift_R);
2249         while (1) {
2250                 WMMaskEvent(dpy, PointerMotionMask | ButtonPressMask | ExposureMask | KeyPressMask, &event);
2251
2252                 if (!checkMouseSamplingRate(&event))
2253                         continue;
2254
2255                 switch (event.type) {
2256                 case KeyPress:
2257                         if ((event.xkey.keycode == shiftl)
2258                             || (event.xkey.keycode == shiftr)) {
2259                                 drawTransparentFrame(wwin, x - width / 2, y - h / 2, width, height);
2260                                 cyclePositionDisplay(wwin, x - width / 2, y - h / 2, width, height);
2261                                 drawTransparentFrame(wwin, x - width / 2, y - h / 2, width, height);
2262                         }
2263                         break;
2264
2265                 case MotionNotify:
2266                         drawTransparentFrame(wwin, x - width / 2, y - h / 2, width, height);
2267
2268                         x = event.xmotion.x_root;
2269                         y = event.xmotion.y_root;
2270
2271                         if (wPreferences.move_display == WDIS_FRAME_CENTER)
2272                                 moveGeometryDisplayCentered(scr, x, y + (height - h) / 2);
2273
2274                         showPosition(wwin, x - width / 2, y - h / 2);
2275
2276                         drawTransparentFrame(wwin, x - width / 2, y - h / 2, width, height);
2277
2278                         break;
2279
2280                 case ButtonPress:
2281                         drawTransparentFrame(wwin, x - width / 2, y - h / 2, width, height);
2282                         XSync(dpy, 0);
2283                         *x_ret = x - width / 2;
2284                         *y_ret = y - h / 2;
2285                         XUngrabPointer(dpy, CurrentTime);
2286                         XUngrabKeyboard(dpy, CurrentTime);
2287                         /* get rid of the geometry window */
2288                         WMUnmapWidget(scr->gview);
2289                         return;
2290
2291                 default:
2292                         WMHandleEvent(&event);
2293                         break;
2294                 }
2295         }
2296 }