/* Copyright (c) 1994-1996 David Hogan, see README for licence details */ #include #include #include #include #include #include "dat.h" #include "fns.h" int nobuttons(XButtonEvent* e) /* Einstuerzende */ { int state; state = (e->state & AllButtonMask); return (e->type == ButtonRelease) && (state & (state - 1)) == 0; } int grab(Window w, Window constrain, int mask, Cursor curs, int t) { int status; if (t == 0) t = timestamp(); status = XGrabPointer( dpy, w, False, mask, GrabModeAsync, GrabModeAsync, constrain, curs, t); return status; } void ungrab(XButtonEvent* e) { XEvent ev; if (!nobuttons(e)) for (;;) { XMaskEvent(dpy, ButtonMask | ButtonMotionMask, &ev); if (ev.type == MotionNotify) continue; e = &ev.xbutton; if (nobuttons(e)) break; } XUngrabPointer(dpy, e->time); curtime = e->time; } static void drawstring( Display* dpy, ScreenInfo* s, Menu* m, int wide, int high, int i, int selected) { int tx, ty; tx = (wide - XTextWidth(font, m->item[i], strlen(m->item[i]))) / 2; ty = i * high + font->ascent + 1; XFillRectangle( dpy, s->menuwin, selected ? s->gcmenubgs : s->gcmenubg, 0, i * high, wide, high); XDrawString( dpy, s->menuwin, selected ? s->gcmenufgs : s->gcmenufg, tx, ty, m->item[i], strlen(m->item[i])); } int menuhit(XButtonEvent* e, Menu* m) { XEvent ev; int i, n, cur, old, wide, high, status, drawn, warp; int x, y, dx, dy, xmax, ymax; ScreenInfo* s; if (font == 0) return -1; s = getscreen(e->root); if (s == 0 || e->window == s->menuwin) /* ugly event mangling */ return -1; dx = 0; for (n = 0; m->item[n]; n++) { wide = XTextWidth(font, m->item[n], strlen(m->item[n])) + 4; if (wide > dx) dx = wide; } wide = dx; cur = m->lasthit; if (cur >= n) cur = n - 1; high = font->ascent + font->descent + 1; dy = n * high; x = e->x - wide / 2; y = e->y - cur * high - high / 2; warp = 0; xmax = DisplayWidth(dpy, s->num); ymax = DisplayHeight(dpy, s->num); if (x < 0) { e->x -= x; x = 0; warp++; } if (x + wide >= xmax) { e->x -= x + wide - xmax; x = xmax - wide; warp++; } if (y < 0) { e->y -= y; y = 0; warp++; } if (y + dy >= ymax) { e->y -= y + dy - ymax; y = ymax - dy; warp++; } if (warp) setmouse(e->x, e->y, s); XMoveResizeWindow(dpy, s->menuwin, x, y, dx, dy); XSelectInput(dpy, s->menuwin, MenuMask); XMapRaised(dpy, s->menuwin); status = grab(s->menuwin, None, MenuGrabMask, None, e->time); if (status != GrabSuccess) { /* graberror("menuhit", status); */ XUnmapWindow(dpy, s->menuwin); return -1; } drawn = 0; for (;;) { XMaskEvent(dpy, MenuMask, &ev); switch (ev.type) { default: fprintf(stderr, "ryudo: menuhit: unknown ev.type %d\n", ev.type); break; case ButtonPress: break; case ButtonRelease: if (ev.xbutton.button != e->button) break; x = ev.xbutton.x; y = ev.xbutton.y; i = y / high; if (cur >= 0 && y >= cur * high - 3 && y < (cur + 1) * high + 3) i = cur; if (x < 0 || x > wide || y < -3) i = -1; else if (i < 0 || i >= n) i = -1; else m->lasthit = i; if (!nobuttons(&ev.xbutton)) i = -1; ungrab(&ev.xbutton); XUnmapWindow(dpy, s->menuwin); return i; case MotionNotify: if (!drawn) break; x = ev.xbutton.x; y = ev.xbutton.y; old = cur; cur = y / high; if (old >= 0 && y >= old * high - 3 && y < (old + 1) * high + 3) cur = old; if (x < 0 || x > wide || y < -3) cur = -1; else if (cur < 0 || cur >= n) cur = -1; if (cur == old) break; if (old >= 0 && old < n) drawstring(dpy, s, m, wide, high, old, 0); if (cur >= 0 && cur < n) drawstring(dpy, s, m, wide, high, cur, 1); break; case Expose: XClearWindow(dpy, s->menuwin); for (i = 0; i < n; i++) drawstring(dpy, s, m, wide, high, i, cur == i); drawn = 1; } } } Client* selectwin(int release, int* shift, ScreenInfo* s) { XEvent ev; XButtonEvent* e; int status; Window w; Client* c; status = grab(s->root, s->root, ButtonMask, s->target, 0); if (status != GrabSuccess) { graberror("selectwin", status); /* */ return 0; } w = None; for (;;) { XMaskEvent(dpy, ButtonMask, &ev); e = &ev.xbutton; switch (ev.type) { case ButtonPress: if (e->button != Button3) { ungrab(e); return 0; } w = e->subwindow; if (!release) { c = getclient(w, 0); if (c == 0) ungrab(e); if (shift != 0) *shift = (e->state & ShiftMask) != 0; return c; } break; case ButtonRelease: ungrab(e); if (e->button != Button3 || e->subwindow != w) return 0; if (shift != 0) *shift = (e->state & ShiftMask) != 0; return getclient(w, 0); } } } int sweepcalc(Client* c, int x, int y, BorderOrient bl, int ignored) { int dx, dy, sx, sy; dx = x - c->x; dy = y - c->y; sx = sy = 1; x += dx; if (dx < 0) { dx = -dx; sx = -1; } y += dy; if (dy < 0) { dy = -dy; sy = -1; } dx -= 2 * BORDER; dy -= 2 * BORDER; if (!c->is9term) { if (dx < c->min_dx) dx = c->min_dx; if (dy < c->min_dy) dy = c->min_dy; } if (c->size.flags & PResizeInc) { dx = c->min_dx + (dx - c->min_dx) / c->size.width_inc * c->size.width_inc; dy = c->min_dy + (dy - c->min_dy) / c->size.height_inc * c->size.height_inc; } if (c->size.flags & PMaxSize) { if (dx > c->size.max_width) dx = c->size.max_width; if (dy > c->size.max_height) dy = c->size.max_height; } c->dx = sx * (dx + 2 * BORDER); c->dy = sy * (dy + 2 * BORDER); return ignored; } int dragcalc(Client* c, int x, int y, BorderOrient bl, int ignored) { c->x += x; c->y += y; return ignored; } int pullcalc(Client* c, int x, int y, BorderOrient bl, int init) { int dx, dy, sx, sy, px, py, spx, spy, rdx, rdy, xoff, yoff, xcorn, ycorn; px = c->x; py = c->y; dx = c->dx; dy = c->dy; sx = sy = 1; spx = spy = 0; xoff = yoff = 0; xcorn = ycorn = 0; switch (bl) { case BorderN: py = y; dy = (c->y + c->dy) - y; spy = 1; yoff = y - c->y; break; case BorderS: dy = y - c->y; yoff = (c->y + c->dy) - y; break; case BorderE: dx = x - c->x; xoff = (c->x + c->dx) - x; break; case BorderW: px = x; dx = (c->x + c->dx) - x; spx = 1; xoff = x - c->x; break; case BorderNNW: case BorderWNW: px = x; dx = (c->x + c->dx) - x; spx = 1; py = y; dy = (c->y + c->dy) - y; spy = 1; xoff = x - c->x; yoff = y - c->y; break; case BorderNNE: case BorderENE: dx = x - c->x; py = y; dy = (c->y + c->dy) - y; spy = 1; xoff = (c->x + c->dx) - x; yoff = y - c->y; break; case BorderSSE: case BorderESE: dx = x - c->x; dy = y - c->y; xoff = (c->x + c->dx) - x; yoff = (c->y + c->dy) - y; break; case BorderSSW: case BorderWSW: px = x; dx = (c->x + c->dx) - x; spx = 1; dy = y - c->y; xoff = x - c->x; yoff = (c->y + c->dy) - y; break; default: break; } switch (bl) { case BorderNNW: case BorderNNE: case BorderSSW: case BorderSSE: xcorn = 1; break; case BorderWNW: case BorderENE: case BorderWSW: case BorderESE: ycorn = 1; break; } if ( !init || xoff < 0 || (xcorn && xoff > CORNER) || (!xcorn && xoff > BORDER) || yoff < 0 || (ycorn && yoff > CORNER) || (!ycorn && yoff > BORDER)) { xoff = 0; yoff = 0; init = 0; } if (debug) fprintf( stderr, "c %dx%d+%d+%d m +%d+%d r %dx%d+%d+%d sp (%d,%d) bl %d\n", c->dx, c->dy, c->x, c->y, x, y, dx, dy, px, py, spx, spy, bl); if (dx < 0) { dx = -dx; sx = -1; } if (dy < 0) { dy = -dy; sy = -1; } /* remember requested size; * after applying size hints we may have to correct position */ rdx = sx * dx; rdy = sy * dy; /* apply size hints */ dx -= (2 * BORDER - xoff); dy -= (2 * BORDER - yoff); if (!c->is9term) { if (dx < c->min_dx) dx = c->min_dx; if (dy < c->min_dy) dy = c->min_dy; } if (c->size.flags & PResizeInc) { dx = c->min_dx + (dx - c->min_dx) / c->size.width_inc * c->size.width_inc; dy = c->min_dy + (dy - c->min_dy) / c->size.height_inc * c->size.height_inc; } if (c->size.flags & PMaxSize) { if (dx > c->size.max_width) dx = c->size.max_width; if (dy > c->size.max_height) dy = c->size.max_height; } /* set size and position */ c->dx = sx * (dx + 2 * BORDER); c->dy = sy * (dy + 2 * BORDER); c->x = px; c->y = py; /* compensate position for size changed due to size hints */ if (spx) c->x -= c->dx - rdx; if (spy) c->y -= c->dy - rdy; return init; } static void xcopy( int fwd, Display* dpy, Drawable src, Drawable dst, GC gc, int x, int y, int dx, int dy, int x1, int y1) { if (fwd) XCopyArea(dpy, src, dst, gc, x, y, dx, dy, x1, y1); else XCopyArea(dpy, dst, src, gc, x1, y1, dx, dy, x, y); } void drawbound(Client* c, int drawing) { int x, y, dx, dy; ScreenInfo* s; if (debug) fprintf( stderr, "drawbound %d %dx%d+%d+%d\n", drawing, c->dx, c->dy, c->x, c->y); s = c->screen; x = c->x; y = c->y; dx = c->dx; dy = c->dy; if (dx < 0) { x += dx; dx = -dx; } if (dy < 0) { y += dy; dy = -dy; } if (dx <= 2 || dy <= 2) return; if (solidsweep) { if (drawing == -1) { XUnmapWindow(dpy, s->sweepwin); return; } x += BORDER; y += BORDER; dx -= 2 * BORDER; dy -= 2 * BORDER; if (drawing) { XMoveResizeWindow(dpy, s->sweepwin, x, y, dx, dy); XSelectInput(dpy, s->sweepwin, MenuMask); XMapRaised(dpy, s->sweepwin); } return; } if (drawing == -1) return; xcopy(drawing, dpy, s->root, s->bkup[0], s->gccopy, x, y, dx, BORDER, 0, 0); xcopy( drawing, dpy, s->root, s->bkup[0], s->gccopy, x, y + dy - BORDER, dx, BORDER, dx, 0); xcopy(drawing, dpy, s->root, s->bkup[1], s->gccopy, x, y, BORDER, dy, 0, 0); xcopy( drawing, dpy, s->root, s->bkup[1], s->gccopy, x + dx - BORDER, y, BORDER, dy, 0, dy); if (drawing) { XFillRectangle(dpy, s->root, s->gcred, x, y, dx, BORDER); XFillRectangle(dpy, s->root, s->gcred, x, y + dy - BORDER, dx, BORDER); XFillRectangle(dpy, s->root, s->gcred, x, y, BORDER, dy); XFillRectangle(dpy, s->root, s->gcred, x + dx - BORDER, y, BORDER, dy); } } void misleep(int msec) { struct timeval t; t.tv_sec = msec / 1000; t.tv_usec = (msec % 1000) * 1000; select(0, 0, 0, 0, &t); } int sweepdrag( Client* c, int but, XButtonEvent* e0, BorderOrient bl, int (*recalc)(Client*, int, int, BorderOrient, int)) { XEvent ev; int idle; int cx, cy, rx, ry; int ox, oy, odx, ody; XButtonEvent* e; int notmoved; notmoved = 1; ox = c->x; oy = c->y; odx = c->dx; ody = c->dy; c->x -= BORDER; c->y -= BORDER; c->dx += 2 * BORDER; c->dy += 2 * BORDER; if (bl != BorderUnknown || e0 == 0) getmouse(&cx, &cy, c->screen); else getmouse(&c->x, &c->y, c->screen); XGrabServer(dpy); if (bl != BorderUnknown) { notmoved = recalc(c, cx, cy, bl, notmoved); } drawbound(c, 1); idle = 0; for (;;) { if (XCheckMaskEvent(dpy, ButtonMask, &ev) == 0) { getmouse(&rx, &ry, c->screen); if (rx != cx || ry != cy || ++idle > 300) { drawbound(c, 0); if (rx == cx && ry == cy) { XUngrabServer(dpy); XFlush(dpy); misleep(500); XGrabServer(dpy); idle = 0; } if (e0 || bl != BorderUnknown) notmoved = recalc(c, rx, ry, bl, notmoved); else notmoved = recalc(c, rx - cx, ry - cy, bl, notmoved); cx = rx; cy = ry; drawbound(c, 1); XFlush(dpy); } misleep(50); continue; } e = &ev.xbutton; switch (ev.type) { case ButtonPress: case ButtonRelease: drawbound(c, 0); ungrab(e); XUngrabServer(dpy); if (e->button != but && c->init) goto bad; if (c->dx < 0) { c->x += c->dx; c->dx = -c->dx; } if (c->dy < 0) { c->y += c->dy; c->dy = -c->dy; } c->x += BORDER; c->y += BORDER; c->dx -= 2 * BORDER; c->dy -= 2 * BORDER; if (c->dx < 4 || c->dy < 4 || c->dx < c->min_dx || c->dy < c->min_dy) goto bad; return 1; } } bad: if (debug) fprintf(stderr, "sweepdrag bad\n"); c->x = ox; c->y = oy; c->dx = odx; c->dy = ody; drawbound(c, -1); return 0; } int sweep(Client* c, int but, XButtonEvent* ignored) { XEvent ev; int status; XButtonEvent* e; ScreenInfo* s; s = c->screen; c->dx = 0; c->dy = 0; status = grab(s->root, s->root, ButtonMask, s->sweep0, 0); if (status != GrabSuccess) { graberror("sweep", status); /* */ return 0; } XMaskEvent(dpy, ButtonMask, &ev); e = &ev.xbutton; if (e->button != but) { ungrab(e); return 0; } XChangeActivePointerGrab(dpy, ButtonMask, s->boxcurs, e->time); return sweepdrag(c, but, e, BorderUnknown, sweepcalc); } int pull(Client* c, int but, XButtonEvent* e) { int status; ScreenInfo* s; BorderOrient bl; bl = borderorient(c, e->x, e->y); /* assert(bl > BorderUnknown && bl < NBorder); */ s = c->screen; status = grab(s->root, s->root, ButtonMask, s->bordcurs[bl], 0); if (status != GrabSuccess) { graberror("pull", status); /* */ return 0; } return sweepdrag(c, but, 0, bl, pullcalc); } int drag(Client* c, int but) { int status; ScreenInfo* s; s = c->screen; status = grab(s->root, s->root, ButtonMask, s->boxcurs, 0); if (status != GrabSuccess) { graberror("drag", status); /* */ return 0; } return sweepdrag(c, but, 0, BorderUnknown, dragcalc); } void getmouse(int* x, int* y, ScreenInfo* s) { Window dw1, dw2; int t1, t2; unsigned int t3; XQueryPointer(dpy, s->root, &dw1, &dw2, x, y, &t1, &t2, &t3); if (debug) fprintf(stderr, "getmouse: %d %d\n", *x, *y); } void setmouse(int x, int y, ScreenInfo* s) { XWarpPointer(dpy, None, s->root, None, None, None, None, x, y); }