/* Copyright ©2006-2010 Kris Maglione * See LICENSE file for license details. */ #include "dat.h" #include #include "fns.h" uint frame_idx(Frame *f) { Frame *fp; uint i; fp = f->area->frame; for(i = 1; fp != f; fp = fp->anext) i++; return i; } Frame* frame_create(Client *c, View *v) { static ushort id = 1; Frame *f; f = emallocz(sizeof *f); f->id = id++; f->client = c; f->view = v; if(c->sel) { f->floatr = c->sel->floatr; f->r = c->sel->r; }else if(c->sel) { f->floatr = c->frame->floatr; f->r = c->frame->r; }else { f->r = client_grav(c, c->r); f->floatr = f->r; c->sel = f; } f->collapsed = false; f->screen = -1; f->oldarea = -1; f->oldscreen = -1; return f; } void frame_remove(Frame *f) { Area *a; a = f->area; if(f->aprev) f->aprev->anext = f->anext; if(f->anext) f->anext->aprev = f->aprev; if(f == a->frame) a->frame = f->anext; if(a->floating) { if(f->sprev) f->sprev->snext = f->snext; if(f->snext) f->snext->sprev = f->sprev; if(f == a->stack) a->stack = f->snext; } f->anext = f->aprev = f->snext = f->sprev = nil; } void frame_insert(Frame *f, Frame *pos) { Area *a; a = f->area; if(pos) { assert(pos != f); f->aprev = pos; f->anext = pos->anext; }else { assert(f->area->frame != f); f->anext = f->area->frame; f->area->frame = f; } if(f->aprev) f->aprev->anext = f; if(f->anext) f->anext->aprev = f; if(a->floating) { assert(f->sprev == nil); frame_restack(f, nil); } } bool frame_restack(Frame *f, Frame *above) { Area *a; a = f->area; if(!a->floating) return false; if(f == above) return false; if(f->sprev || f == a->stack) if(f->sprev == above) return false; if(f->sprev) f->sprev->snext = f->snext; else if(f->snext) a->stack = f->snext; if(f->snext) f->snext->sprev = f->sprev; f->sprev = above; if(above == nil) { f->snext = a->stack; a->stack = f; } else { f->snext = above->snext; above->snext = f; } if(f->snext) f->snext->sprev = f; assert(f->snext != f && f->sprev != f); return true; } /* Handlers */ static bool bup_event(Window *w, void *aux, XButtonEvent *e) { if((e->state & def.mod) != def.mod) XAllowEvents(display, ReplayPointer, e->time); else XUngrabPointer(display, e->time); if(!e->subwindow) event("ClientClick %#C %d\n", aux, e->button); return false; } static bool bdown_event(Window *w, void *aux, XButtonEvent *e) { Frame *f; Client *c; c = aux; f = c->sel; if((e->state & def.mod) == def.mod) { switch(e->button) { case Button1: focus(c, false); mouse_resize(c, Center, true); break; case Button2: frame_restack(f, nil); view_restack(f->view); focus(c, false); grabpointer(c->framewin, nil, cursor[CurNone], ButtonReleaseMask); break; case Button3: focus(c, false); mouse_resize(c, quadrant(f->r, Pt(e->x_root, e->y_root)), true); break; default: XAllowEvents(display, ReplayPointer, e->time); break; } }else { if(e->button == Button1) { if(!e->subwindow) { frame_restack(f, nil); view_restack(f->view); mouse_checkresize(f, Pt(e->x, e->y), true); } if(f->client != selclient()) focus(c, false); } if(e->subwindow) XAllowEvents(display, ReplayPointer, e->time); else { /* Ungrab so a menu can receive events before the button is released */ XUngrabPointer(display, e->time); sync(); event("ClientMouseDown %#C %d\n", f->client, e->button); } } return false; } static bool enter_event(Window *w, void *aux, XCrossingEvent *e) { Client *c; Frame *f; c = aux; f = c->sel; if(disp.focus != c || selclient() != c) { Dprint(DFocus, "%E\n", e); Dprint(DFocus, "enter_notify(f) => [%#C]%s%s\n", f->client, f->client->name, e->serial <= event_lastconfigure ? " (ignored)" : ""); if(e->detail != NotifyInferior) if(e->serial > event_lastconfigure && !f->collapsed) focus(f->client, false); } mouse_checkresize(f, Pt(e->x, e->y), false); return false; } static bool expose_event(Window *w, void *aux, XExposeEvent *e) { Client *c; USED(e); c = aux; if(c->sel) frame_draw(c->sel); return false; } static bool motion_event(Window *w, void *aux, XMotionEvent *e) { Client *c; c = aux; mouse_checkresize(c->sel, Pt(e->x, e->y), false); return false; } Handlers framehandler = { .bup = bup_event, .bdown = bdown_event, .enter = enter_event, .expose = expose_event, .motion = motion_event, }; WinHints frame_gethints(Frame *f) { WinHints h; Client *c; Rectangle r; Point d; int minh; minh = labelh(def.font); c = f->client; h = *c->w.hints; r = frame_client2rect(c, ZR, f->area->floating); d = subpt(r.max, r.min); if(!f->area->floating && def.incmode == IIgnore) h.inc = Pt(1, 1); if(h.min.x < 2*minh) h.min.x = minh + (2*minh) % h.inc.x; if(h.min.y < minh) h.min.y = minh + minh % h.inc.y; h.min.x += d.x; h.min.y += d.y; /* Guard against overflow. */ h.max.x = max(h.max.x + d.x, h.max.x); h.max.y = max(h.max.y + d.y, h.max.y); h.base.x += d.x; h.base.y += d.y; h.baspect.x += d.x; h.baspect.y += d.y; h.group = 0; h.grav = ZP; h.gravstatic = 0; h.position = 0; return h; } #define ADJ(PE, ME) \ if(c->fullscreen >= 0) \ return r; \ \ if(!floating) { \ r.min.x PE 1; \ r.min.y PE labelh(def.font); \ r.max.x ME 1; \ r.max.y ME 1; \ }else { \ if(!c->borderless) { \ r.min.x PE def.border; \ r.max.x ME def.border; \ r.max.y ME def.border; \ } \ if(!c->titleless) \ r.min.y PE labelh(def.font); \ } \ Rectangle frame_rect2client(Client *c, Rectangle r, bool floating) { ADJ(+=, -=) /* Force clients to be at least 1x1 */ r.max.x = max(r.max.x, r.min.x+1); r.max.y = max(r.max.y, r.min.y+1); return r; } Rectangle frame_client2rect(Client *c, Rectangle r, bool floating) { ADJ(-=, +=) return r; } #undef ADJ void frame_resize(Frame *f, Rectangle r) { Client *c; Rectangle fr, cr; int collapsed, dx; if(btassert("8 full", Dx(r) <= 0 || Dy(r) < 0 || Dy(r) == 0 && (!f->area->max || resizing) && !f->collapsed)) { fprint(2, "Frame rect: %R\n", r); r.max.x = max(r.min.x+1, r.max.x); r.max.y = max(r.min.y+1, r.max.y); } c = f->client; if(c->fullscreen >= 0) { f->r = screens[c->fullscreen]->r; f->crect = rectsetorigin(f->r, ZP); return; } /* if(f->area->floating) f->collapsed = false; */ fr = frame_hints(f, r, get_sticky(f->r, r)); if(f->area->floating && !c->strut) fr = constrain(fr, -1); /* Collapse managed frames which are too small */ /* XXX. */ collapsed = f->collapsed; if(!f->area->floating && f->area->mode == Coldefault) { f->collapsed = false; if(Dy(r) < 2 * labelh(def.font)) f->collapsed = true; } if(collapsed != f->collapsed) ewmh_updatestate(c); fr.max.x = max(fr.max.x, fr.min.x + 2*labelh(def.font)); if(f->collapsed && f->area->floating) fr.max.y = fr.min.y + labelh(def.font); cr = frame_rect2client(c, fr, f->area->floating); if(f->area->floating) f->r = fr; else { f->r = r; dx = Dx(r) - Dx(cr); dx -= 2 * (cr.min.x - fr.min.x); cr.min.x += dx / 2; cr.max.x += dx / 2; } f->crect = rectsubpt(cr, f->r.min); if(f->area->floating && !f->collapsed) f->floatr = f->r; } static void pushlabel(Image *img, Rectangle *rp, char *s, CTuple *col) { Rectangle r; int w; w = textwidth(def.font, s) + def.font->height; w = min(w, Dx(*rp) - 30); /* Magic number. */ if(w > 0) { r = *rp; r.min.x = r.max.x - w; rp->max.x -= w; if(0) drawline(img, Pt(rp->max.x, r.min.y+2), Pt(rp->max.x, r.max.y-2), CapButt, 1, &col->border); drawstring(img, def.font, r, East, s, &col->fg); } free(s); } void frame_draw(Frame *f) { Rectangle r, fr; Client *c; CTuple *col; Image *img; char *s; int n, m; if(f == nil || f->view != selview || f->area == nil) return; c = f->client; img = c->framewin->depth == 32 ? disp.ibuf32 : disp.ibuf; fr = rectsetorigin(c->framewin->r, ZP); /* Pick colors. */ if((c == selclient() || c == disp.focus) && disp.sel) col = &def.focuscolor; else col = &def.normcolor; /* Background/border */ r = fr; fill(img, r, &col->bg); border(img, r, 1, &col->border); /* Title border */ r.max.y = r.min.y + labelh(def.font); border(img, r, 1, &col->border); f->titlebar = insetrect(r, 3); f->titlebar.max.y += 3; f->grabbox = insetrect(r, 2); f->grabbox.max.x = f->grabbox.min.x + Dy(f->grabbox); /* Odd focus. Unselected, with keyboard focus. */ /* Draw a border just inside the titlebar. */ if(c != selclient() && c == disp.focus) { border(img, insetrect(r, 1), 1, &def.normcolor.bg); border(img, insetrect(r, 2), 1, &def.focuscolor.border); } if(c->urgent) fill(img, f->grabbox, &col->fg); border(img, f->grabbox, 1, &col->border); /* Odd focus. Selected, without keyboard focus. */ /* Draw a border around the grabbox. */ if(c != disp.focus && col == &def.focuscolor) border(img, insetrect(r, -1), 1, &def.normcolor.bg); /* Draw a border on borderless+titleless selected apps. */ if(c->borderless && c->titleless && f->area->floating && !c->fullscreen && c == selclient()) setborder(c->framewin, def.border, &def.focuscolor.border); else setborder(c->framewin, 0, &def.focuscolor.border); /* Label */ r = Rect(f->grabbox.max.x, 0, fr.max.x, labelh(def.font)); /* Draw count on frames in 'max' columns. */ if(f->area->max && !resizing) { n = stack_count(f, &m); pushlabel(img, &r, smprint("%d/%d", m, n), col); } /* Label clients with extra tags. */ if((s = client_extratags(c))) pushlabel(img, &r, s, col); if(f->area->floating) /* Make sure floating clients have room for their indicators. */ r.max.x -= f->grabbox.max.x; if(!ewmh_responsive_p(c)) r.min.x += drawstring(img, def.font, r, West, "(wedged) ", &col->fg); r.min.x += drawstring(img, def.font, r, West, c->name, &col->fg); /* Draw inner border on floating clients. */ if(f->area->floating) { r.min.x += 10; r.max.x += Dx(f->grabbox); r.min.y = f->grabbox.min.y; r.max.y = f->grabbox.max.y; border(img, r, 1, &col->border); } /* Border increment gaps... */ r.min.y = f->crect.min.y; r.min.x = max(1, f->crect.min.x - 1); r.max.x = min(fr.max.x - 1, f->crect.max.x + 1); r.max.y = min(fr.max.y - 1, f->crect.max.y + 1); border(img, r, 1, &col->border); /* Why? Because some non-ICCCM-compliant apps feel the need to * change the background properties of all of their ancestor windows * in order to implement pseudo-transparency. * What's more, the designers of X11 felt that it would be unfair to * implementers to make it possible to detect, or forbid, such changes. */ XSetWindowBackgroundPixmap(display, c->framewin->xid, None); copyimage(c->framewin, fr, img, ZP); } void frame_draw_all(void) { Client *c; for(c=client; c; c=c->next) if(c->sel && c->sel->view == selview) frame_draw(c->sel); } void frame_swap(Frame *fa, Frame *fb) { Frame **fp; Client *c; if(fa == fb) return; for(fp = &fa->client->frame; *fp; fp = &fp[0]->cnext) if(*fp == fa) break; fp[0] = fp[0]->cnext; for(fp = &fb->client->frame; *fp; fp = &fp[0]->cnext) if(*fp == fb) break; fp[0] = fp[0]->cnext; c = fa->client; fa->client = fb->client; fb->client = c; fb->cnext = c->frame; c->frame = fb; c = fa->client; fa->cnext = c->frame; c->frame = fa; if(c->sel) view_update(c->sel->view); } void move_focus(Frame *old_f, Frame *f) { int noinput; noinput = (old_f && old_f->client->noinput) || (f && f->client->noinput) || disp.hasgrab != &c_root; if(noinput) { if(old_f) frame_draw(old_f); if(f) frame_draw(f); } } void frame_focus(Frame *f) { Frame *old_f, *ff; View *v; Area *a, *old_a; v = f->view; a = f->area; old_a = v->sel; if(0 && f->collapsed) { for(ff=f; ff->collapsed && ff->anext; ff=ff->anext) ; for(; ff->collapsed && ff->aprev; ff=ff->aprev) ; /* XXX */ f->colr.max.y = f->colr.min.y + Dy(ff->colr); ff->colr.max.y = ff->colr.min.y + labelh(def.font); } else if(f->area->mode == Coldefault) { /* XXX */ for(; f->collapsed && f->anext; f=f->anext) ; for(; f->collapsed && f->aprev; f=f->aprev) ; } old_f = old_a->sel; a->sel = f; if(a != old_a) area_focus(f->area); if(old_a != v->oldsel && f != old_f) v->oldsel = nil; if(f->area->floating) f->collapsed = false; if(v == selview && a == v->sel && !resizing) { move_focus(old_f, f); if(a->floating) float_arrange(a); // if(!a->floating && ((a->mode == Colstack) || (a->mode == Colmax))) if(true) column_arrange(a, false); client_focus(f->client); } } int frame_delta_h(void) { return def.border + labelh(def.font); } Rectangle constrain(Rectangle r, int inset) { WMScreen **sp; WMScreen *s, *sbest; Rectangle isect; Point p; int best, n; if(inset < 0) inset = Dy(screen->brect); /* * FIXME: This will cause problems for windows with * D(r) < 2 * inset */ SET(best); sbest = nil; for(sp=screens; (s = *sp); sp++) { if(!screen->showing) continue; isect = rect_intersection(r, insetrect(s->r, inset)); if(Dx(isect) >= 0 && Dy(isect) >= 0) return r; if(Dx(isect) <= 0 && Dy(isect) <= 0) n = max(Dx(isect), Dy(isect)); else n = min(Dx(isect), Dy(isect)); if(!sbest || n > best) { sbest = s; best = n; } } isect = insetrect(sbest->r, inset); p = ZP; p.x -= min(r.max.x - isect.min.x, 0); p.x -= max(r.min.x - isect.max.x, 0); p.y -= min(r.max.y - isect.min.y, 0); p.y -= max(r.min.y - isect.max.y, 0); return rectaddpt(r, p); }