/* 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); }