| /* Copyright (c) 1994-1996 David Hogan, see README for licence details */ |
| #include <stdio.h> |
| #include <X11/X.h> |
| #include <X11/Xos.h> |
| #include <X11/Xlib.h> |
| #include <X11/Xutil.h> |
| #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, "rio: 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); |
| } |