/* (C)opyright MMVI Kris Maglione * See LICENSE file for license details. */ #include #include #include #include "wmii.h" enum { ButtonMask = ButtonPressMask | ButtonReleaseMask, MouseMask = ButtonMask | PointerMotionMask }; static void rect_morph_xy(XRectangle *rect, int dx, int dy, BlitzAlign *mask) { BlitzAlign new_mask = 0; if(*mask & NORTH) { if(rect->height - dy >= 0 || *mask & SOUTH) { rect->y += dy; rect->height -= dy; } else { rect->y += rect->height; rect->height = dy - rect->height; new_mask ^= NORTH|SOUTH; } } if(*mask & SOUTH) { if(rect->height + dy >= 0 || *mask & NORTH) rect->height += dy; else { rect->height = -dy - rect->height; rect->y -= rect->height; new_mask ^= NORTH|SOUTH; } } if(*mask & EAST) { if(rect->width + dx >= 0 || *mask & WEST) rect->width += dx; else { rect->width = -dx - rect->width; rect->x -= rect->width; new_mask ^= EAST|WEST; } } if(*mask & WEST) { if(rect->width - dx >= 0 || *mask & EAST) { rect->x += dx; rect->width -= dx; } else { rect->x += rect->width; rect->width = dx - rect->width; new_mask ^= EAST|WEST; } } *mask ^= new_mask; } typedef struct { XRectangle *rects; int num; int x1, y1, x2, y2; BlitzAlign mask; int *delta; } SnapArgs; static void snap_line(SnapArgs *a) { int i, t_xy; /* horizontal */ if(a->y1 == a->y2 && (a->mask & (NORTH|SOUTH))) { for(i=0; i < a->num; i++) { if(!(r_east(&a->rects[i]) < a->x1) || (a->rects[i].x > a->x2)) { if(abs(a->rects[i].y - a->y1) <= abs(*a->delta)) *a->delta = a->rects[i].y - a->y1; t_xy = r_south(&a->rects[i]); if(abs(t_xy - a->y1) < abs(*a->delta)) *a->delta = t_xy - a->y1; } } } else if (a->mask & (EAST|WEST)) { /* This is the same as above, tr/xy/yx/, * s/width/height/, s/height/width/ */ for(i=0; i < a->num; i++) { if(!(r_south(&a->rects[i]) < a->y1) || (a->rects[i].y > a->y2)) { if(abs(a->rects[i].x - a->x1) <= abs(*a->delta)) *a->delta = a->rects[i].x - a->x1; t_xy = r_east(&a->rects[i]); if(abs(t_xy - a->x1) < abs(*a->delta)) *a->delta = t_xy - a->x1; } } } } BlitzAlign snap_rect(XRectangle *rects, int num, XRectangle *current, BlitzAlign *mask, int snap) { SnapArgs a = { rects, num, 0, 0, 0, 0, *mask, nil }; int dx, dy; BlitzAlign ret; dx = dy = snap + 1; a.x1 = current->x; a.x2 = r_east(current); a.delta = &dy; if(*mask & NORTH) { a.y2 = a.y1 = current->y; snap_line(&a); } if(*mask & SOUTH) { a.y2 = a.y1 = r_south(current); snap_line(&a); } a.y1 = current->y; a.y2 = r_south(current); a.delta = &dx; if(*mask & EAST) { a.x1 = a.x2 = r_east(current); snap_line(&a); } if(*mask & WEST) { a.x1 = a.x2 = current->x; snap_line(&a); } rect_morph_xy(current, abs(dx) <= snap ? dx : 0, abs(dy) <= snap ? dy : 0, mask); ret = *mask; if(abs(dx) <= snap) ret ^= EAST|WEST; if(abs(dy) <= snap) ret ^= NORTH|SOUTH; return ret ^ CENTER; } static void draw_xor_border(XRectangle *r) { XRectangle xor; xor = *r; xor.x += 2; xor.y += 2; xor.width = xor.width > 4 ? xor.width - 4 : 0; xor.height = xor.height > 4 ? xor.height - 4 : 0; XSetLineAttributes(blz.dpy, xorgc, 1, LineSolid, CapNotLast, JoinMiter); XSetForeground(blz.dpy, xorgc, def.focuscolor.bg); if(xor.height > 4 && xor.width > 2) XDrawLine(blz.dpy, blz.root, xorgc, xor.x + 2, xor.y + xor.height / 2, r_east(&xor) - 2, xor.y + xor.height / 2); if(xor.width > 4 && xor.height > 2) XDrawLine(blz.dpy, blz.root, xorgc, xor.x + xor.width / 2, xor.y + 2, xor.x + xor.width / 2, r_south(&xor) - 2); XSetLineAttributes(blz.dpy, xorgc, 4, LineSolid, CapNotLast, JoinMiter); XDrawRectangles(blz.dpy, blz.root, xorgc, &xor, 1); } static void find_droppoint(Frame *frame, int x, int y, XRectangle *rect, Bool do_move) { View *v; Area *a, *a_prev; Frame *f, *f_close; v = frame->view; rect->y = 0; rect->height = screen->rect.height - screen->brect.height; a_prev = v->area; for(a = a_prev->next; a && a->next; a = a->next) { if(x < r_east(&a->rect)) break; a_prev = a; } if(x < (a->rect.x + labelh(&def.font))) { rect->x = a->rect.x - 4; rect->width = 8; if(do_move) { a = new_column(v, a_prev, 0); send_to_area(a, frame); focus(frame->client, False); } return; } if(x > (r_east(&a->rect) - labelh(&def.font))) { rect->x = r_east(&a->rect) - 4; rect->width = 8; if(do_move) { a = new_column(v, a, 0); send_to_area(a, frame); focus(frame->client, False); } return; } rect->x = a->rect.x; rect->width = a->rect.width; f_close = nil; for(f = a->frame; f; f = f->anext) { if(y < f->rect.y) break; if(y < r_south(&f->rect)) break; f_close = f; } if(f == nil) f = f_close; if(y < (f->rect.y + labelh(&def.font))) { rect->y = f->rect.y; rect->height = 2; if(f_close) { rect->y = r_south(&f_close->rect); rect->height = f->rect.y - rect->y; } if(do_move) goto do_move; return; } if(y > r_south(&f->rect) - labelh(&def.font)) { rect->y = r_south(&f->rect); rect->height = (screen->rect.height - labelh(&def.font) - rect->y); if(f->anext) rect->height = (f->anext->rect.y - rect->y); if(do_move) goto do_move; return; } *rect = f->rect; if(do_move) { swap_frames(frame, f); focus(frame->client, False); } return; do_move: if(frame == f) return; if(a != frame->area) send_to_area(a, frame); remove_frame(frame); insert_frame(f, frame, False); arrange_column(f->area, False); focus(frame->client, True); } static void do_managed_move(Client *c) { XRectangle frect, ofrect; Window dummy; XEvent ev; Frame *f; uint di; int x, y, i; focus(c, False); f = c->sel; XSync(blz.dpy, False); if(XGrabPointer(blz.dpy, c->framewin, False, MouseMask, GrabModeAsync, GrabModeAsync, None, cursor[CurMove], CurrentTime) != GrabSuccess) return; XGrabServer(blz.dpy); XQueryPointer(blz.dpy, blz.root, &dummy, &dummy, &i, &i, &x, &y, &di); find_droppoint(f, x, y, &frect, False); draw_xor_border(&frect); for(;;) { XMaskEvent(blz.dpy, MouseMask | ExposureMask, &ev); switch (ev.type) { case ButtonRelease: draw_xor_border(&frect); find_droppoint(f, x, y, &frect, True); XUngrabServer(blz.dpy); XUngrabPointer(blz.dpy, CurrentTime); XSync(blz.dpy, False); return; case MotionNotify: ofrect = frect; x = ev.xmotion.x_root; y = ev.xmotion.y_root; find_droppoint(f, x, y, &frect, False); if(memcmp(&frect, &ofrect, sizeof(frect))) { draw_xor_border(&ofrect); draw_xor_border(&frect); } break; case Expose: (handler[Expose])(&ev); break; default: break; } } } void do_mouse_resize(Client *c, Bool grabbox, BlitzAlign align) { BlitzAlign grav; Window dummy; Cursor cur; XEvent ev; XRectangle *rects, ofrect, frect, origin; int snap, dx, dy, pt_x, pt_y, hr_x, hr_y, i; uint num, di; Bool floating; float rx, ry; Frame *f; f = c->sel; floating = f->area->floating; origin = frect = f->rect; cur = cursor_of_quad(align); if(floating) { rects = rects_of_view(f->area->view, &num, (grabbox ? c->frame : nil)); snap = screen->rect.height / 66; }else{ rects = nil; snap = 0; } if(align == CENTER) { if(!grabbox) cur = cursor[CurInvisible]; if(!floating) { do_managed_move(c); return; } } XQueryPointer(blz.dpy, c->framewin, &dummy, &dummy, &i, &i, &pt_x, &pt_y, &di); rx = (float)pt_x / frect.width; ry = (float)pt_y / frect.height; if(XGrabPointer( /* display */ blz.dpy, /* window */ c->framewin, /* owner_events */ False, /* event_mask */ MouseMask, /* pointer_mode */ GrabModeAsync, /* keyboard_mode */ GrabModeAsync, /* confine_to */ None, /* cursor */ cur, /* time */ CurrentTime ) != GrabSuccess) return; XQueryPointer(blz.dpy, blz.root, &dummy, &dummy, &i, &i, &pt_x, &pt_y, &di); if(align != CENTER) { hr_x = dx = frect.width / 2; hr_y = dy = frect.height / 2; if(align&NORTH) dy -= hr_y; if(align&SOUTH) dy += hr_y; if(align&EAST) dx += hr_x; if(align&WEST) dx -= hr_x; XTranslateCoordinates(blz.dpy, c->framewin, blz.root, dx, dy, &pt_x, &pt_y, &dummy); XWarpPointer(blz.dpy, None, blz.root, 0, 0, 0, 0, pt_x, pt_y); } else if(!grabbox) { hr_x = screen->rect.width / 2; hr_y = screen->rect.height / 2; XWarpPointer(blz.dpy, None, blz.root, 0, 0, 0, 0, hr_x, hr_y); while(XCheckMaskEvent(blz.dpy, PointerMotionMask, &ev)); } XSync(blz.dpy, False); if(!grabbox) { XGrabServer(blz.dpy); draw_xor_border(&frect); }else unmap_client(c, IconicState); for(;;) { XMaskEvent(blz.dpy, MouseMask | ExposureMask, &ev); switch (ev.type) { case ButtonRelease: if(!grabbox) draw_xor_border(&frect); if(!floating) resize_column(c, &frect); else resize_client(c, &frect); if(!grabbox) { if(align != CENTER) XTranslateCoordinates(blz.dpy, c->framewin, blz.root, (frect.width * rx), (frect.height * ry), &pt_x, &pt_y, &dummy); if(pt_y > screen->brect.y) pt_y = screen->brect.y - 1; XWarpPointer(blz.dpy, None, blz.root, 0, 0, 0, 0, pt_x, pt_y); XUngrabServer(blz.dpy); }else map_client(c); if(rects) free(rects); XUngrabPointer(blz.dpy, CurrentTime); XSync(blz.dpy, False); return; case MotionNotify: ofrect = frect; dx = ev.xmotion.x_root; dy = ev.xmotion.y_root; if(align == CENTER && !grabbox) { if(dx == hr_x && dy == hr_y) continue; XWarpPointer(blz.dpy, None, blz.root, 0, 0, 0, 0, hr_x, hr_y); dx -= hr_x; dy -= hr_y; }else{ dx -= pt_x; dy -= pt_y; } pt_x += dx; pt_y += dy; rect_morph_xy(&origin, dx, dy, &align); if(align != CENTER) check_frame_constraints(&origin); frect = origin; if(floating) grav = snap_rect(rects, num, &frect, &align, snap); else grav = align ^ CENTER; apply_sizehints(c, &frect, floating, True, grav); if(grabbox) { XMoveWindow(blz.dpy, c->framewin, frect.x, frect.y); XSync(blz.dpy, False); } else { draw_xor_border(&ofrect); draw_xor_border(&frect); } break; case Expose: (handler[Expose])(&ev); break; default: break; } } } void grab_button(Window w, uint button, ulong mod) { XGrabButton(blz.dpy, button, mod, w, False, ButtonMask, GrabModeSync, GrabModeSync, None, None); if((mod != AnyModifier) && num_lock_mask) { XGrabButton(blz.dpy, button, mod | num_lock_mask, w, False, ButtonMask, GrabModeSync, GrabModeAsync, None, None); XGrabButton(blz.dpy, button, mod | num_lock_mask | LockMask, w, False, ButtonMask, GrabModeSync, GrabModeAsync, None, None); } }