blob: 5aff69c8134c2897e5d1ebaeb42c8532eabab311 [file] [log] [blame]
/* 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);
}