Initial revision
[wmaker-crm.git] / src / moveres.c
blob3924e1df74eb2466d3a1584c11a03ad540c99b90
1 /*
2 * WindowMaker window manager
3 *
4 * Copyright (c) 1997, 1998 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.
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.
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.
22 #include "wconfig.h"
24 #include <X11/Xlib.h>
25 #include <X11/Xutil.h>
26 #include <X11/keysym.h>
27 #include <stdio.h>
28 #include <unistd.h>
30 #include "WindowMaker.h"
31 #include "wcore.h"
32 #include "framewin.h"
33 #include "window.h"
34 #include "client.h"
35 #include "icon.h"
36 #include "funcs.h"
37 #include "actions.h"
38 #include "workspace.h"
40 #include "list.h"
42 /* How many different types of geometry/position
43 display thingies are there? */
44 #define NUM_DISPLAYS 4
46 #define LEFT 1
47 #define RIGHT 2
48 #define HORIZONTAL (LEFT|RIGHT)
49 #define UP 4
50 #define DOWN 8
51 #define VERTICAL (UP|DOWN)
53 /****** Global Variables ******/
54 extern Time LastTimestamp;
56 extern Cursor wCursor[WCUR_LAST];
58 extern WPreferences wPreferences;
60 extern Atom _XA_WM_PROTOCOLS;
62 /** Locals **/
63 static LinkedList *wSelectedWindows=NULL;
67 void
68 wGetGeometryWindowSize(WScreen *scr, unsigned int *width,
69 unsigned int *height)
71 #ifdef I18N_MB
72 *width = XmbTextEscapement(scr->info_text_font->font, "-8888 x -8888", 13);
73 *height = (3 * scr->info_text_font->height) / 2;
74 #else
75 *width = XTextWidth(scr->info_text_font->font, "-8888 x -8888", 13);
76 *height = (3 * scr->info_text_font->font->ascent) / 2;
77 #endif
82 *----------------------------------------------------------------------
83 * moveGeometryDisplayCentered
85 * routine that moves the geometry/position window on scr so it is
86 * centered over the given coordinates (x,y). Also the window position
87 * is clamped so it stays on the screen at all times.
88 *----------------------------------------------------------------------
90 static void
91 moveGeometryDisplayCentered(WScreen *scr, int x, int y)
93 x -= scr->geometry_display_width / 2;
94 y -= scr->geometry_display_height / 2;
96 if (x < 1)
97 x = 1;
98 else if (x > (scr->scr_width - scr->geometry_display_width - 3))
99 x = scr->scr_width - scr->geometry_display_width - 3;
101 if (y < 1)
102 y = 1;
103 else if (y > (scr->scr_height - scr->geometry_display_height - 3))
104 y = scr->scr_height - scr->geometry_display_height - 3;
106 XMoveWindow(dpy, scr->geometry_display, x, y);
110 static void
111 showPosition(WWindow *wwin, int x, int y)
113 WScreen *scr = wwin->screen_ptr;
114 GC gc = scr->info_text_gc;
115 char num[16];
116 int fw, fh;
118 if (wPreferences.move_display == WDIS_NEW) {
119 #if 0
120 int width = wwin->frame->core->width;
121 int height = wwin->frame->core->height;
123 GC lgc = scr->line_gc;
124 XSetForeground(dpy, lgc, scr->line_pixel);
125 sprintf(num, "%i", x);
127 XDrawLine(dpy, scr->root_win, lgc, 0, y-1, scr->scr_width, y-1);
128 XDrawLine(dpy, scr->root_win, lgc, 0, y+height+2, scr->scr_width,
129 y+height+2);
130 XDrawLine(dpy, scr->root_win, lgc, x-1, 0, x-1, scr->scr_height);
131 XDrawLine(dpy, scr->root_win, lgc, x+width+2, 0, x+width+2,
132 scr->scr_height);
133 #endif
134 } else {
135 XClearWindow(dpy, scr->geometry_display);
136 sprintf(num, "%+i %-+i", x, y);
137 fw = wTextWidth(scr->info_text_font->font, num, strlen(num));
139 XSetForeground(dpy, gc, scr->window_title_pixel[WS_UNFOCUSED]);
141 fh = scr->info_text_font->height;
142 wDrawString(scr->geometry_display, scr->info_text_font, gc,
143 (scr->geometry_display_width - 2 - fw) / 2,
144 (scr->geometry_display_height-fh)/2 + scr->info_text_font->y,
145 num, strlen(num));
150 static void
151 cyclePositionDisplay(WWindow *wwin, int x, int y, int w, int h)
153 WScreen *scr = wwin->screen_ptr;
155 wPreferences.move_display++;
156 wPreferences.move_display %= NUM_DISPLAYS;
158 if (wPreferences.move_display == WDIS_NEW) {
159 XUnmapWindow(dpy, scr->geometry_display);
160 } else {
161 if (wPreferences.move_display == WDIS_CENTER) {
162 moveGeometryDisplayCentered(scr,
163 scr->scr_width/2, scr->scr_height/2);
164 } else if (wPreferences.move_display == WDIS_TOPLEFT) {
165 moveGeometryDisplayCentered(scr, 1, 1);
166 } else if (wPreferences.move_display == WDIS_FRAME_CENTER) {
167 moveGeometryDisplayCentered(scr, x + w/2, y + h/2);
169 XMapRaised(dpy, scr->geometry_display);
170 showPosition(wwin, x, y);
175 static void
176 mapPositionDisplay(WWindow *wwin, int x, int y, int w, int h)
178 WScreen *scr = wwin->screen_ptr;
180 if (wPreferences.move_display == WDIS_NEW) {
181 return;
182 } else if (wPreferences.move_display == WDIS_CENTER) {
183 moveGeometryDisplayCentered(scr, scr->scr_width / 2,
184 scr->scr_height / 2);
185 } else if (wPreferences.move_display == WDIS_TOPLEFT) {
186 moveGeometryDisplayCentered(scr, 1, 1);
187 } else if (wPreferences.move_display == WDIS_FRAME_CENTER) {
188 moveGeometryDisplayCentered(scr, x + w/2, y + h/2);
190 XMapRaised(dpy, scr->geometry_display);
191 showPosition(wwin, x, y);
194 #define unmapPositionDisplay(w) \
195 XUnmapWindow(dpy, (w)->screen_ptr->geometry_display);
198 static void
199 showGeometry(WWindow *wwin, int x1, int y1, int x2, int y2, int direction)
201 WScreen *scr = wwin->screen_ptr;
202 Window root = scr->root_win;
203 GC gc = scr->line_gc;
204 int ty, by, my, x, y, mx, s;
205 char num[16];
206 XSegment segment[4];
207 int fw, fh;
209 ty = y1 + wwin->frame->top_width;
210 by = y2 - wwin->frame->bottom_width;
211 fw = wTextWidth(scr->info_text_font->font, "8888", 4);
212 fh = scr->info_text_font->height;
214 if (wPreferences.size_display == WDIS_NEW) {
215 XSetForeground(dpy, gc, scr->line_pixel);
217 /* vertical geometry */
218 if (((direction & LEFT) && (x2 < scr->scr_width - fw)) || (x1 < fw)) {
219 x = x2;
220 s = -15;
221 } else {
222 x = x1;
223 s = 15;
225 my = (ty + by) / 2;
227 /* top arrow */
228 /* end bar */
229 segment[0].x1 = x - (s + 6); segment[0].y1 = ty;
230 segment[0].x2 = x - (s - 10); segment[0].y2 = ty;
232 /* arrowhead */
233 segment[1].x1 = x - (s - 2); segment[1].y1 = ty + 1;
234 segment[1].x2 = x - (s - 5); segment[1].y2 = ty + 7;
236 segment[2].x1 = x - (s - 2); segment[2].y1 = ty + 1;
237 segment[2].x2 = x - (s + 1); segment[2].y2 = ty + 7;
239 /* line */
240 segment[3].x1 = x - (s - 2); segment[3].y1 = ty + 1;
241 segment[3].x2 = x - (s - 2); segment[3].y2 = my - fh/2 - 1;
243 XDrawSegments(dpy, root, gc, segment, 4);
245 /* bottom arrow */
246 /* end bar */
247 segment[0].y1 = by;
248 segment[0].y2 = by;
250 /* arrowhead */
251 segment[1].y1 = by - 1;
252 segment[1].y2 = by - 7;
254 segment[2].y1 = by - 1;
255 segment[2].y2 = by - 7;
257 /* line */
258 segment[3].y1 = my + fh/2 + 2;
259 segment[3].y2 = by - 1;
261 XDrawSegments(dpy, root, gc, segment, 4);
263 sprintf(num, "%i", (by - ty - wwin->normal_hints->base_height) /
264 wwin->normal_hints->height_inc);
265 fw = wTextWidth(scr->info_text_font->font, num, strlen(num));
267 /* XSetForeground(dpy, gc, scr->window_title_pixel[WS_UNFOCUSED]); */
269 /* Display the height. */
270 wDrawString(root, scr->info_text_font, gc,
271 x - s + 3 - fw/2, my - fh/2 + scr->info_text_font->y + 1,
272 num, strlen(num));
273 XSetForeground(dpy, gc, scr->line_pixel);
274 /* horizontal geometry */
275 if (y1 < 15) {
276 y = y2;
277 s = -15;
278 } else {
279 y = y1;
280 s = 15;
282 mx = x1 + (x2 - x1)/2;
283 sprintf(num, "%i", (x2 - x1 - wwin->normal_hints->base_width) /
284 wwin->normal_hints->width_inc);
285 fw = wTextWidth(scr->info_text_font->font, num, strlen(num));
287 /* left arrow */
288 /* end bar */
289 segment[0].x1 = x1; segment[0].y1 = y - (s + 6);
290 segment[0].x2 = x1; segment[0].y2 = y - (s - 10);
292 /* arrowhead */
293 segment[1].x1 = x1 + 7; segment[1].y1 = y - (s + 1);
294 segment[1].x2 = x1 + 1; segment[1].y2 = y - (s - 2);
296 segment[2].x1 = x1 + 1; segment[2].y1 = y - (s - 2);
297 segment[2].x2 = x1 + 7; segment[2].y2 = y - (s - 5);
299 /* line */
300 segment[3].x1 = x1 + 1; segment[3].y1 = y - (s - 2);
301 segment[3].x2 = mx - fw/2 - 2; segment[3].y2 = y - (s - 2);
303 XDrawSegments(dpy, root, gc, segment, 4);
305 /* right arrow */
306 /* end bar */
307 segment[0].x1 = x2 + 1;
308 segment[0].x2 = x2 + 1;
310 /* arrowhead */
311 segment[1].x1 = x2 - 6;
312 segment[1].x2 = x2;
314 segment[2].x1 = x2;
315 segment[2].x2 = x2 - 6;
317 /* line */
318 segment[3].x1 = mx + fw/2 + 2;
319 segment[3].x2 = x2;
321 XDrawSegments(dpy, root, gc, segment, 4);
323 /* XSetForeground(dpy, gc, scr->window_title_pixel[WS_UNFOCUSED]); */
325 /* Display the width. */
326 wDrawString(root, scr->info_text_font, gc,
327 mx - fw/2 + 1, y - s + fh/2 + 1, num, strlen(num));
328 } else {
329 XClearWindow(dpy, scr->geometry_display);
330 sprintf(num, "%i x %-i", (x2 - x1 - wwin->normal_hints->base_width)
331 / wwin->normal_hints->width_inc,
332 (by - ty - wwin->normal_hints->base_height)
333 / wwin->normal_hints->height_inc);
334 fw = wTextWidth(scr->info_text_font->font, num, strlen(num));
336 XSetForeground(dpy, scr->info_text_gc,
337 scr->window_title_pixel[WS_UNFOCUSED]);
339 /* Display the height. */
340 wDrawString(scr->geometry_display, scr->info_text_font,
341 scr->info_text_gc,
342 (scr->geometry_display_width-fw)/2,
343 (scr->geometry_display_height-fh)/2 +scr->info_text_font->y,
344 num, strlen(num));
349 static void
350 cycleGeometryDisplay(WWindow *wwin, int x, int y, int w, int h, int dir)
352 WScreen *scr = wwin->screen_ptr;
354 wPreferences.size_display++;
355 wPreferences.size_display %= NUM_DISPLAYS;
357 if (wPreferences.size_display == WDIS_NEW) {
358 XUnmapWindow(dpy, scr->geometry_display);
359 } else {
360 if (wPreferences.size_display == WDIS_CENTER) {
361 moveGeometryDisplayCentered(scr,
362 scr->scr_width / 2, scr->scr_height / 2);
363 } else if (wPreferences.size_display == WDIS_TOPLEFT) {
364 moveGeometryDisplayCentered(scr, 1, 1);
365 } else if (wPreferences.size_display == WDIS_FRAME_CENTER) {
366 moveGeometryDisplayCentered(scr, x + w/2, y + h/2);
368 XMapRaised(dpy, scr->geometry_display);
369 showGeometry(wwin, x, y, x + w, y + h, dir);
374 static void
375 mapGeometryDisplay(WWindow *wwin, int x, int y, int w, int h)
377 WScreen *scr = wwin->screen_ptr;
379 if (wPreferences.size_display == WDIS_NEW)
380 return;
382 if (wPreferences.size_display == WDIS_CENTER) {
383 moveGeometryDisplayCentered(scr, scr->scr_width / 2,
384 scr->scr_height / 2);
385 } else if (wPreferences.size_display == WDIS_TOPLEFT) {
386 moveGeometryDisplayCentered(scr, 1, 1);
387 } else if (wPreferences.size_display == WDIS_FRAME_CENTER) {
388 moveGeometryDisplayCentered(scr, x + w/2, y + h/2);
390 XMapRaised(dpy, scr->geometry_display);
391 showGeometry(wwin, x, y, x + w, y + h, 0);
394 #define unmapGeometryDisplay(w) \
395 XUnmapWindow(dpy, (w)->screen_ptr->geometry_display);
397 static void
398 checkEdgeResistance(WWindow *wwin, int *winx, int *winy, int off_x, int off_y)
400 int scr_width = wwin->screen_ptr->scr_width;
401 int scr_height = wwin->screen_ptr->scr_height;
402 int x = *winx;
403 int y = *winy;
404 int edge_resistance = wPreferences.edge_resistance;
406 x -= off_x;
407 y -= off_y;
409 if ((x + wwin->frame->core->width) >= (scr_width - 2)) {
410 if ((x + wwin->frame->core->width) < ((scr_width - 2)
411 + edge_resistance)) {
412 x = scr_width - wwin->frame->core->width - 2;
413 } else {
414 x -= edge_resistance;
418 if (x <= 0) {
419 if (x > -edge_resistance) {
420 x = 0;
421 } else {
422 x += edge_resistance;
426 if ((y + wwin->frame->core->height) >= (scr_height - 1)) {
427 if ((y + wwin->frame->core->height) < ((scr_height - 1)
428 + edge_resistance)) {
429 y = scr_height - wwin->frame->core->height - 1;
430 } else {
431 y -= edge_resistance;
435 if (y <=0) {
436 if (y > -edge_resistance) {
437 y = 0;
438 } else {
439 y += edge_resistance;
444 *winx = x;
445 *winy = y;
448 static void
449 doWindowMove(WWindow *wwin, int single_win_x, int single_win_y,
450 LinkedList *list, int dx, int dy, int off_x, int off_y)
452 WWindow *tmpw;
453 int x, y;
454 int scr_width = wwin->screen_ptr->scr_width;
455 int scr_height = wwin->screen_ptr->scr_height;
457 if (!list) {
458 checkEdgeResistance(wwin, &single_win_x, &single_win_y, off_x, off_y);
459 wWindowMove(wwin, single_win_x, single_win_y);
460 } else {
461 while (list) {
462 tmpw = list->head;
463 x = tmpw->frame_x + dx;
464 y = tmpw->frame_y + dy;
466 /* don't let windows become unreachable */
468 if (x + (int)tmpw->frame->core->width < 20)
469 x = 20 - (int)tmpw->frame->core->width;
470 else if (x + 20 > scr_width)
471 x = scr_width - 20;
473 if (y + (int)tmpw->frame->core->height < 20)
474 y = 20 - (int)tmpw->frame->core->height;
475 else if (y + 20 > scr_height)
476 y = scr_height - 20;
478 wWindowMove(tmpw, x, y);
479 list = list->tail;
485 static void
486 drawTransparentFrame(WWindow *wwin, int x, int y, int width, int height)
488 Window root = wwin->screen_ptr->root_win;
489 GC gc = wwin->screen_ptr->frame_gc;
490 int h = 0;
491 int bottom = 0;
493 if (!wwin->window_flags.no_titlebar && !wwin->flags.shaded) {
494 h = wwin->screen_ptr->title_font->height + TITLEBAR_EXTRA_HEIGHT;
496 if (!wwin->window_flags.no_resizebar && !wwin->flags.shaded) {
497 /* Can't use wwin-frame->bottom_width because, in some cases
498 (e.g. interactive placement), frame does not point to anything. */
499 bottom = RESIZEBAR_HEIGHT - 1;
501 XDrawRectangle(dpy, root, gc, x, y, width + 1, height + 1);
503 if (h > 0) {
504 XDrawLine(dpy, root, gc, x + 1, y + h, x + width + 1, y + h);
506 if (bottom > 0) {
507 XDrawLine(dpy, root, gc, x + 1,
508 y + height - bottom,
509 x + width + 1,
510 y + height - bottom);
515 static void
516 drawFrames(WWindow *wwin, LinkedList *list, int dx, int dy, int off_x, int off_y)
518 WWindow *tmpw;
519 int scr_width = wwin->screen_ptr->scr_width;
520 int scr_height = wwin->screen_ptr->scr_height;
521 int x, y;
523 if (!list) {
525 x = wwin->frame_x + dx;
526 y = wwin->frame_y + dy;
528 checkEdgeResistance(wwin, &x, &y, off_x, off_y);
529 drawTransparentFrame(wwin, x, y,
530 wwin->frame->core->width,
531 wwin->frame->core->height);
533 } else {
534 while (list) {
535 tmpw = list->head;
536 x = tmpw->frame_x + dx;
537 y = tmpw->frame_y + dy;
539 /* don't let windows become unreachable */
541 if (x + (int)tmpw->frame->core->width < 20)
542 x = 20 - (int)tmpw->frame->core->width;
543 else if (x + 20 > scr_width)
544 x = scr_width - 20;
546 if (y + (int)tmpw->frame->core->height < 20)
547 y = 20 - (int)tmpw->frame->core->height;
548 else if (y + 20 > scr_height)
549 y = scr_height - 20;
551 drawTransparentFrame(tmpw, x, y, tmpw->frame->core->width,
552 tmpw->frame->core->height);
554 list = list->tail;
561 static void
562 flushMotion()
564 XEvent ev;
566 XSync(dpy, 0);
567 while (XCheckMaskEvent(dpy, ButtonMotionMask, &ev)) ;
571 static void
572 crossWorkspace(WScreen *scr, WWindow *wwin, int opaque_move,
573 int new_workspace, int rewind)
575 /* do not let window be unmapped */
576 if (opaque_move) {
577 wwin->flags.changing_workspace = 1;
578 wWindowChangeWorkspace(wwin, new_workspace);
580 /* go to new workspace */
581 wWorkspaceChange(scr, new_workspace);
583 wwin->flags.changing_workspace = 0;
585 if (rewind)
586 XWarpPointer(dpy, None, None, 0, 0, 0, 0, scr->scr_width - 20, 0);
587 else
588 XWarpPointer(dpy, None, None, 0, 0, 0, 0, -(scr->scr_width - 20), 0);
590 flushMotion();
592 if (!opaque_move) {
593 XGrabPointer(dpy, scr->root_win, True, PointerMotionMask
594 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
595 GrabModeAsync, None, wCursor[WCUR_MOVE], CurrentTime);
601 *----------------------------------------------------------------------
602 * wMouseMoveWindow--
603 * Move the named window and the other selected ones (if any),
604 * interactively. Also shows the position of the window, if only one
605 * window is being moved.
606 * If the window is not on the selected window list, the selected
607 * windows are deselected.
608 * If shift is pressed during the operation, the position display
609 * is changed to another type.
611 * Returns:
612 * True if the window was moved, False otherwise.
614 * Side effects:
615 * The window(s) position is changed, and the client(s) are
616 * notified about that.
617 * The position display configuration may be changed.
618 *----------------------------------------------------------------------
621 wMouseMoveWindow(WWindow *wwin, XEvent *ev)
623 WScreen *scr = wwin->screen_ptr;
624 XEvent event;
625 Window root = scr->root_win;
626 KeyCode shiftl, shiftr;
627 int w = wwin->frame->core->width;
628 int h = wwin->frame->core->height;
629 int x = wwin->frame_x;
630 int y = wwin->frame_y;
631 int ox, oy, orig_x, orig_y;
632 int off_x, off_y;
633 short count = 0; /* for automatic workspace creation */
634 int started = 0;
635 int warped = 0;
636 /* This needs not to change while moving, else bad things can happen */
637 int opaque_move = wPreferences.opaque_move;
638 int XOffset, YOffset, origDragX, origDragY;
640 origDragX = wwin->frame_x;
641 origDragY = wwin->frame_y;
642 XOffset = origDragX - ev->xbutton.x_root;
643 YOffset = origDragY - ev->xbutton.y_root;
645 if (!wwin->flags.selected) {
646 /* this window is not selected, unselect others and move only wwin */
647 wUnselectWindows();
649 orig_x = ox = ev->xbutton.x_root;
650 orig_y = oy = ev->xbutton.y_root;
651 off_x = x; off_y = y;
652 checkEdgeResistance(wwin, &off_x, &off_y, 0, 0);
653 off_x = (off_x-x); off_y = (off_y-y);
654 #ifdef DEBUG
655 puts("Moving window");
656 #endif
657 shiftl = XKeysymToKeycode(dpy, XK_Shift_L);
658 shiftr = XKeysymToKeycode(dpy, XK_Shift_R);
659 while (1) {
660 if (warped) {
661 int junk;
662 Window junkw;
664 /* XWarpPointer() doesn't seem to generate Motion events, so
665 we've got to simulate them */
666 XQueryPointer(dpy, root, &junkw, &junkw, &event.xmotion.x_root,
667 &event.xmotion.y_root, &junk, &junk,
668 (unsigned *) &junk);
669 } else {
670 Window win;
672 WMMaskEvent(dpy, KeyPressMask | ButtonMotionMask
673 | ButtonReleaseMask | ButtonPressMask | ExposureMask, &event);
675 if (event.type == MotionNotify) {
676 /* compress MotionNotify events */
677 win = event.xmotion.window;
678 while (XCheckMaskEvent(dpy, ButtonMotionMask, &event)) ;
681 switch (event.type) {
682 case KeyPress:
683 if (wSelectedWindows)
684 break;
685 if ((event.xkey.keycode == shiftl || event.xkey.keycode == shiftr)
686 && started) {
687 if (!opaque_move)
688 drawFrames(wwin, wSelectedWindows,
689 ox - orig_x, oy - orig_y, off_x, off_y);
691 cyclePositionDisplay(wwin, x, y, w, h);
693 if (!opaque_move) {
694 drawFrames(wwin, wSelectedWindows,
695 ox - orig_x, oy - orig_y, off_x, off_y);
697 showPosition(wwin, x, y);
699 break;
701 case MotionNotify:
702 if (started) {
703 showPosition(wwin, x, y);
705 if (!opaque_move) {
706 drawFrames(wwin, wSelectedWindows,
707 ox-orig_x, oy-orig_y, off_x, off_y);
708 } else {
709 doWindowMove(wwin, event.xmotion.x_root + XOffset,
710 event.xmotion.y_root + YOffset,
711 wSelectedWindows,
712 event.xmotion.x_root - ox,
713 event.xmotion.y_root - oy,
714 off_x, off_y);
717 x = event.xmotion.x_root + XOffset;
718 y = event.xmotion.y_root + YOffset;
720 checkEdgeResistance(wwin, &x, &y, off_x, off_y);
722 if (!wSelectedWindows) {
723 if (wPreferences.move_display == WDIS_FRAME_CENTER)
724 moveGeometryDisplayCentered(scr, x + w/2, y + h/2);
726 if (!warped && !wPreferences.no_autowrap) {
727 if (event.xmotion.x_root <= 1) {
728 if (scr->current_workspace > 0) {
729 crossWorkspace(scr, wwin, opaque_move,
730 scr->current_workspace-1, True);
731 warped = 1;
732 count = 0;
733 } else if (scr->current_workspace == 0
734 && wPreferences.ws_cycle) {
735 crossWorkspace(scr, wwin, opaque_move,
736 scr->workspace_count-1, True);
737 warped = 1;
738 count = 0;
740 } else if (event.xmotion.x_root >= scr->scr_width - 2) {
742 if (scr->current_workspace == scr->workspace_count-1) {
743 if ((!wPreferences.ws_advance && wPreferences.ws_cycle)
744 || (scr->workspace_count == MAX_WORKSPACES)) {
745 crossWorkspace(scr, wwin, opaque_move, 0, False);
746 warped = 1;
747 count = 0;
749 /* if user insists on trying to go to next
750 workspace even when it's already the last,
751 create a new one */
752 else if ((ox == event.xmotion.x_root)
753 && wPreferences.ws_advance) {
755 /* detect user "rubbing" the window
756 against the edge */
757 if (count > 0
758 && oy - event.xmotion.y_root > MOVE_THRESHOLD)
759 count = -(count + 1);
760 else if (count <= 0
761 && event.xmotion.y_root - oy > MOVE_THRESHOLD)
762 count = -count + 1;
764 /* create a new workspace */
765 if (abs(count) > 2) {
766 /* go to next workspace */
767 wWorkspaceNew(scr);
769 crossWorkspace(scr, wwin, opaque_move,
770 scr->current_workspace+1, False);
771 warped = 1;
772 count = 0;
774 } else if (scr->current_workspace < scr->workspace_count) {
776 /* go to next workspace */
777 crossWorkspace(scr, wwin, opaque_move,
778 scr->current_workspace+1, False);
779 warped = 1;
780 count = 0;
782 } else {
783 count = 0;
785 } else {
786 warped = 0;
788 } else if (abs(orig_x - event.xmotion.x_root) >= MOVE_THRESHOLD
789 || abs(orig_y - event.xmotion.y_root) >= MOVE_THRESHOLD) {
790 XChangeActivePointerGrab(dpy, ButtonMotionMask
791 | ButtonReleaseMask | ButtonPressMask,
792 wCursor[WCUR_MOVE], CurrentTime);
793 started = 1;
794 XGrabKeyboard(dpy, root, False, GrabModeAsync, GrabModeAsync,
795 CurrentTime);
796 event.xmotion.x_root = orig_x;
797 event.xmotion.y_root = orig_y;
799 if (!wSelectedWindows)
800 mapPositionDisplay(wwin, x, y, w, h);
802 if (!opaque_move)
803 XGrabServer(dpy);
805 ox = event.xmotion.x_root;
806 oy = event.xmotion.y_root;
808 if (started && !opaque_move)
809 drawFrames(wwin, wSelectedWindows, ox - orig_x, oy - orig_y, off_x, off_y);
811 showPosition(wwin, x, y);
812 break;
814 case ButtonPress:
815 break;
817 case ButtonRelease:
818 if (event.xbutton.button != ev->xbutton.button)
819 break;
821 if (started) {
822 if (!opaque_move) {
823 drawFrames(wwin, wSelectedWindows,
824 ox - orig_x, oy - orig_y, off_x, off_y);
825 XSync(dpy, 0);
826 doWindowMove(wwin, event.xmotion.x_root + XOffset,
827 event.xmotion.y_root + YOffset,
828 wSelectedWindows,
829 ox - orig_x, oy - orig_y,
830 off_x, off_y);
832 #ifndef CONFIGURE_WINDOW_WHILE_MOVING
833 wWindowSynthConfigureNotify(wwin);
834 #endif
836 XUngrabKeyboard(dpy, CurrentTime);
837 XUngrabServer(dpy);
838 if (!opaque_move) {
839 wWindowChangeWorkspace(wwin, scr->current_workspace);
840 wSetFocusTo(scr, wwin);
842 showPosition(wwin, x, y);
843 if (!wSelectedWindows) {
844 /* get rid of the geometry window */
845 unmapPositionDisplay(wwin);
848 #ifdef DEBUG
849 puts("End move window");
850 #endif
851 return started;
853 default:
854 if (started && !opaque_move) {
855 drawFrames(wwin, wSelectedWindows, ox - orig_x, oy - orig_y, off_x, off_y);
856 XUngrabServer(dpy);
857 WMHandleEvent(&event);
858 XSync(dpy, False);
859 XGrabServer(dpy);
860 drawFrames(wwin, wSelectedWindows, ox - orig_x, oy - orig_y, off_x, off_y);
861 } else {
862 WMHandleEvent(&event);
864 break;
867 return 0;
871 #define RESIZEBAR 1
872 #define HCONSTRAIN 2
874 static int
875 getResizeDirection(WWindow *wwin, int x, int y, int dx, int dy,
876 int flags)
878 int w = wwin->frame->core->width - 1;
879 int cw = wwin->frame->resizebar_corner_width;
880 int dir;
882 /* if not resizing through the resizebar */
883 if (!(flags & RESIZEBAR)) {
884 int xdir = (abs(x) < (wwin->client.width/2)) ? LEFT : RIGHT;
885 int ydir = (abs(y) < (wwin->client.height/2)) ? UP : DOWN;
886 if (abs(dx) < 2 || abs(dy) < 2) {
887 if (abs(dy) > abs(dx))
888 xdir = 0;
889 else
890 ydir = 0;
892 return (xdir | ydir);
895 /* window is too narrow. allow diagonal resize */
896 if (cw * 2 >= w) {
897 int ydir;
899 if (flags & HCONSTRAIN)
900 ydir = 0;
901 else
902 ydir = DOWN;
903 if (x < cw)
904 return (LEFT | ydir);
905 else
906 return (RIGHT | ydir);
908 /* vertical resize */
909 if ((x > cw) && (x < w - cw))
910 return DOWN;
912 if (x < cw)
913 dir = LEFT;
914 else
915 dir = RIGHT;
917 if ((abs(dy) > 0) && !(flags & HCONSTRAIN))
918 dir |= DOWN;
920 return dir;
924 void
925 wMouseResizeWindow(WWindow *wwin, XEvent *ev)
927 XEvent event;
928 WScreen *scr = wwin->screen_ptr;
929 Window root = scr->root_win;
930 int vert_border = wwin->frame->top_width + wwin->frame->bottom_width;
931 int fw = wwin->frame->core->width;
932 int fh = wwin->frame->core->height;
933 int fx = wwin->frame_x;
934 int fy = wwin->frame_y;
935 int is_resizebar = (wwin->frame->resizebar
936 && ev->xany.window==wwin->frame->resizebar->window);
937 int orig_x, orig_y;
938 int started;
939 int dw, dh;
940 int rw = fw, rh = fh;
941 int rx1, ry1, rx2, ry2;
942 int res = 0;
943 KeyCode shiftl, shiftr;
944 int h = 0;
945 int orig_fx = fx;
946 int orig_fy = fy;
947 int orig_fw = fw;
948 int orig_fh = fh;
950 if (wwin->flags.shaded) {
951 wwarning("internal error: tryein");
952 return;
954 orig_x = ev->xbutton.x_root;
955 orig_y = ev->xbutton.y_root;
957 started = 0;
958 #ifdef DEBUG
959 puts("Resizing window");
960 #endif
962 wUnselectWindows();
963 rx1 = fx;
964 rx2 = fx + fw - 1;
965 ry1 = fy;
966 ry2 = fy + fh - 1;
967 shiftl = XKeysymToKeycode(dpy, XK_Shift_L);
968 shiftr = XKeysymToKeycode(dpy, XK_Shift_R);
969 if (!wwin->window_flags.no_titlebar)
970 h = wwin->screen_ptr->title_font->height + TITLEBAR_EXTRA_HEIGHT;
971 else
972 h = 0;
973 while (1) {
974 WMMaskEvent(dpy, KeyPressMask | ButtonMotionMask | ButtonReleaseMask
975 | ButtonPressMask | ExposureMask, &event);
976 switch (event.type) {
977 case KeyPress:
978 showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
979 if ((event.xkey.keycode == shiftl || event.xkey.keycode == shiftr)
980 && started) {
981 drawTransparentFrame(wwin, fx, fy, fw, fh);
982 cycleGeometryDisplay(wwin, fx, fy, fw, fh, res);
983 drawTransparentFrame(wwin, fx, fy, fw, fh);
985 showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
986 break;
988 case MotionNotify:
989 if (started) {
990 dw = 0;
991 dh = 0;
993 orig_fx = fx;
994 orig_fy = fy;
995 orig_fw = fw;
996 orig_fh = fh;
998 if (res & LEFT)
999 dw = orig_x - event.xmotion.x_root;
1000 else if (res & RIGHT)
1001 dw = event.xmotion.x_root - orig_x;
1002 if (res & UP)
1003 dh = orig_y - event.xmotion.y_root;
1004 else if (res & DOWN)
1005 dh = event.xmotion.y_root - orig_y;
1007 orig_x = event.xmotion.x_root;
1008 orig_y = event.xmotion.y_root;
1010 rw += dw;
1011 rh += dh;
1012 fw = rw;
1013 fh = rh - vert_border;
1014 wWindowConstrainSize(wwin, &fw, &fh);
1015 fh += vert_border;
1016 if (res & LEFT)
1017 fx = rx2 - fw + 1;
1018 else if (res & RIGHT)
1019 fx = rx1;
1020 if (res & UP)
1021 fy = ry2 - fh + 1;
1022 else if (res & DOWN)
1023 fy = ry1;
1024 } else if (abs(orig_x - event.xmotion.x_root) >= MOVE_THRESHOLD
1025 || abs(orig_y - event.xmotion.y_root) >= MOVE_THRESHOLD) {
1026 int tx, ty;
1027 Window junkw;
1028 int flags;
1030 XTranslateCoordinates(dpy, root, wwin->frame->core->window,
1031 orig_x, orig_y, &tx, &ty, &junkw);
1033 /* check if resizing through resizebar */
1034 if (is_resizebar)
1035 flags = RESIZEBAR;
1036 else
1037 flags = 0;
1039 if (is_resizebar && ((ev->xbutton.state & ShiftMask)
1040 || abs(orig_y - event.xmotion.y_root) < HRESIZE_THRESHOLD))
1041 flags |= HCONSTRAIN;
1043 res = getResizeDirection(wwin, tx, ty,
1044 orig_x - event.xmotion.x_root,
1045 orig_y - event.xmotion.y_root, flags);
1047 XChangeActivePointerGrab(dpy, ButtonMotionMask
1048 | ButtonReleaseMask | ButtonPressMask,
1049 wCursor[WCUR_RESIZE], CurrentTime);
1050 XGrabKeyboard(dpy, root, False, GrabModeAsync, GrabModeAsync,
1051 CurrentTime);
1053 XGrabServer(dpy);
1055 /* Draw the resize frame for the first time. */
1056 mapGeometryDisplay(wwin, fx, fy, fw, fh);
1058 drawTransparentFrame(wwin, fx, fy, fw, fh);
1060 showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
1062 started = 1;
1064 if (started) {
1065 if (wPreferences.size_display == WDIS_FRAME_CENTER) {
1066 drawTransparentFrame(wwin, orig_fx, orig_fy,
1067 orig_fw, orig_fh);
1068 moveGeometryDisplayCentered(scr, fx + fw / 2, fy + fh / 2);
1069 drawTransparentFrame(wwin, fx, fy, fw, fh);
1070 } else {
1071 drawTransparentFrame(wwin, orig_fx, orig_fy,
1072 orig_fw, orig_fh);
1073 drawTransparentFrame(wwin, fx, fy, fw, fh);
1075 if (fh != orig_fh || fw != orig_fw) {
1076 if (wPreferences.size_display == WDIS_NEW) {
1077 showGeometry(wwin, orig_fx, orig_fy, orig_fx + orig_fw,
1078 orig_fy + orig_fh, res);
1080 showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
1083 break;
1085 case ButtonPress:
1086 break;
1088 case ButtonRelease:
1089 if (event.xbutton.button != ev->xbutton.button)
1090 break;
1092 if (started) {
1093 showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
1095 drawTransparentFrame(wwin, fx, fy, fw, fh);
1097 XUngrabKeyboard(dpy, CurrentTime);
1098 unmapGeometryDisplay(wwin);
1099 XUngrabServer(dpy);
1100 wWindowConfigure(wwin, fx, fy, fw, fh - vert_border);
1102 #ifdef DEBUG
1103 puts("End resize window");
1104 #endif
1105 return;
1107 default:
1108 WMHandleEvent(&event);
1113 #undef LEFT
1114 #undef RIGHT
1115 #undef HORIZONTAL
1116 #undef UP
1117 #undef DOWN
1118 #undef VERTICAL
1119 #undef HCONSTRAIN
1120 #undef RESIZEBAR
1122 void
1123 wUnselectWindows()
1125 WWindow *wwin;
1127 while (wSelectedWindows) {
1128 wwin = wSelectedWindows->head;
1129 if (wwin->flags.miniaturized && wwin->icon && wwin->icon->selected)
1130 wIconSelect(wwin->icon);
1132 XSetWindowBorder(dpy, wwin->frame->core->window,
1133 wwin->screen_ptr->frame_border_pixel);
1134 wwin->flags.selected = 0;
1135 list_remove_head(&wSelectedWindows);
1140 void
1141 wSelectWindow(WWindow *wwin)
1143 if (!wwin->flags.selected) {
1144 wwin->flags.selected = 1;
1145 XSetWindowBorder(dpy, wwin->frame->core->window,
1146 wwin->screen_ptr->white_pixel);
1147 wSelectedWindows = list_cons(wwin, wSelectedWindows);
1148 } else {
1149 wwin->flags.selected = 0;
1150 XSetWindowBorder(dpy, wwin->frame->core->window,
1151 wwin->screen_ptr->frame_border_pixel);
1152 wSelectedWindows = list_remove_elem(wSelectedWindows, wwin);
1157 static void
1158 selectWindowsInside(WScreen *scr, int x1, int y1, int x2, int y2)
1160 WWindow *tmpw;
1162 /* select the windows and put them in the selected window list */
1163 tmpw = scr->focused_window;
1164 while (tmpw != NULL) {
1165 if (!(tmpw->flags.miniaturized || tmpw->flags.hidden)) {
1166 if ((tmpw->frame->workspace == scr->current_workspace
1167 || tmpw->window_flags.omnipresent)
1168 && (tmpw->frame_x >= x1) && (tmpw->frame_y >= y1)
1169 && (tmpw->frame->core->width + tmpw->frame_x <= x2)
1170 && (tmpw->frame->core->height + tmpw->frame_y <= y2)) {
1171 XSetWindowBorder(dpy, tmpw->frame->core->window,
1172 tmpw->screen_ptr->white_pixel);
1173 tmpw->flags.selected = 1;
1174 wSelectedWindows = list_cons(tmpw, wSelectedWindows);
1177 tmpw = tmpw->prev;
1182 void
1183 wSelectWindows(WScreen *scr, XEvent *ev)
1185 XEvent event;
1186 Window root = scr->root_win;
1187 GC gc = scr->frame_gc;
1188 int xp = ev->xbutton.x_root;
1189 int yp = ev->xbutton.y_root;
1190 int w = 0, h = 0;
1191 int x = xp, y = yp;
1193 #ifdef DEBUG
1194 puts("Selecting windows");
1195 #endif
1196 if (XGrabPointer(dpy, scr->root_win, False, ButtonMotionMask
1197 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
1198 GrabModeAsync, None, wCursor[WCUR_DEFAULT],
1199 CurrentTime) != Success) {
1200 return;
1202 XGrabServer(dpy);
1204 wUnselectWindows();
1206 XDrawRectangle(dpy, root, gc, xp, yp, w, h);
1207 while (1) {
1208 WMMaskEvent(dpy, ButtonReleaseMask | PointerMotionMask
1209 | ButtonPressMask, &event);
1211 switch (event.type) {
1212 case MotionNotify:
1213 XDrawRectangle(dpy, root, gc, x, y, w, h);
1214 x = event.xmotion.x_root;
1215 if (x < xp) {
1216 w = xp - x;
1217 } else {
1218 w = x - xp;
1219 x = xp;
1221 y = event.xmotion.y_root;
1222 if (y < yp) {
1223 h = yp - y;
1224 } else {
1225 h = y - yp;
1226 y = yp;
1228 XDrawRectangle(dpy, root, gc, x, y, w, h);
1229 break;
1231 case ButtonPress:
1232 break;
1234 case ButtonRelease:
1235 if (event.xbutton.button != ev->xbutton.button)
1236 break;
1238 XDrawRectangle(dpy, root, gc, x, y, w, h);
1239 XUngrabServer(dpy);
1240 XUngrabPointer(dpy, CurrentTime);
1241 selectWindowsInside(scr, x, y, x + w, y + h);
1242 #ifdef DEBUG
1243 puts("End window selection");
1244 #endif
1245 return;
1247 default:
1248 WMHandleEvent(&event);
1254 void
1255 InteractivePlaceWindow(WWindow *wwin, int *x_ret, int *y_ret)
1257 WScreen *scr = wwin->screen_ptr;
1258 Window root = scr->root_win;
1259 int x, y, h = 0;
1260 int width = wwin->client.width;
1261 int height = wwin->client.height;
1262 XEvent event;
1263 KeyCode shiftl, shiftr;
1264 Window junkw;
1265 int junk;
1267 if (XGrabPointer(dpy, root, True, PointerMotionMask | ButtonPressMask,
1268 GrabModeAsync, GrabModeAsync, None,
1269 wCursor[WCUR_DEFAULT], CurrentTime) != Success) {
1270 *x_ret = 0;
1271 *y_ret = 0;
1272 return;
1274 if (!wwin->window_flags.no_titlebar) {
1275 h = scr->title_font->height + TITLEBAR_EXTRA_HEIGHT;
1276 height += h;
1278 if (!wwin->window_flags.no_resizebar) {
1279 height += RESIZEBAR_HEIGHT;
1281 XGrabKeyboard(dpy, root, False, GrabModeAsync, GrabModeAsync, CurrentTime);
1282 XQueryPointer(dpy, root, &junkw, &junkw, &x, &y, &junk, &junk,
1283 (unsigned *) &junk);
1284 mapPositionDisplay(wwin, x - width/2, y - h/2, width, height);
1286 drawTransparentFrame(wwin, x - width/2, y - h/2, width, height);
1288 shiftl = XKeysymToKeycode(dpy, XK_Shift_L);
1289 shiftr = XKeysymToKeycode(dpy, XK_Shift_R);
1290 while (1) {
1291 WMMaskEvent(dpy, PointerMotionMask|ButtonPressMask|ExposureMask|KeyPressMask,
1292 &event);
1293 switch (event.type) {
1294 case KeyPress:
1295 if ((event.xkey.keycode == shiftl)
1296 || (event.xkey.keycode == shiftr)) {
1297 drawTransparentFrame(wwin,
1298 x - width/2, y - h/2, width, height);
1299 cyclePositionDisplay(wwin,
1300 x - width/2, y - h/2, width, height);
1301 drawTransparentFrame(wwin,
1302 x - width/2, y - h/2, width, height);
1304 break;
1306 case MotionNotify:
1307 drawTransparentFrame(wwin, x - width/2, y - h/2, width, height);
1309 x = event.xmotion.x_root;
1310 y = event.xmotion.y_root;
1312 if (wPreferences.move_display == WDIS_FRAME_CENTER)
1313 moveGeometryDisplayCentered(scr, x, y + (height - h) / 2);
1315 showPosition(wwin, x - width/2, y - h/2);
1317 drawTransparentFrame(wwin, x - width/2, y - h/2, width, height);
1319 break;
1321 case ButtonPress:
1322 drawTransparentFrame(wwin, x - width/2, y - h/2, width, height);
1323 XSync(dpy, 0);
1324 *x_ret = x - width/2;
1325 *y_ret = y - h/2;
1326 XUngrabPointer(dpy, CurrentTime);
1327 XUngrabKeyboard(dpy, CurrentTime);
1328 /* get rid of the geometry window */
1329 unmapPositionDisplay(wwin);
1330 return;
1332 default:
1333 WMHandleEvent(&event);
1334 break;