/* Copyright (c) 2008, 2009 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) * Micah Cowan (micah@cowan.name) * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) * Copyright (c) 1987 Oliver Laumann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING); if not, see * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA * **************************************************************** */ #include "config.h" #include "screen.h" #include "extern.h" #include "canvas.h" #include "list_generic.h" extern struct display *display; extern struct win *fore, *windows; extern struct layer *flayer; extern int captionalways; extern struct LayFuncs BlankLf; extern int focusminwidth, focusminheight; static void CanvasInitBlank(cv) struct canvas *cv; { cv->c_blank.l_cvlist = cv; cv->c_blank.l_width = cv->c_xe - cv->c_xs + 1; cv->c_blank.l_height = cv->c_ye - cv->c_ys + 1; cv->c_blank.l_x = cv->c_blank.l_y = 0; cv->c_blank.l_layfn = &BlankLf; cv->c_blank.l_data = 0; cv->c_blank.l_next = 0; cv->c_blank.l_bottom = &cv->c_blank; cv->c_blank.l_blocking = 0; cv->c_layer = &cv->c_blank; } static void FreePerp(pcv) struct canvas *pcv; { struct canvas *cv; if (!pcv->c_slperp) return; cv = pcv->c_slperp; cv->c_slprev = pcv->c_slprev; if (cv->c_slprev) cv->c_slprev->c_slnext = cv; cv->c_slback = pcv->c_slback; if (cv->c_slback && cv->c_slback->c_slperp == pcv) cv->c_slback->c_slperp = cv; cv->c_slorient = pcv->c_slorient; cv->c_slweight = pcv->c_slweight; while (cv->c_slnext) { cv = cv->c_slnext; cv->c_slorient = pcv->c_slorient; cv->c_slback = pcv->c_slback; cv->c_slweight = pcv->c_slweight; } cv->c_slnext = pcv->c_slnext; if (cv->c_slnext) cv->c_slnext->c_slprev = cv; LayerCleanupMemory(&pcv->c_blank); free(pcv); } void FreeCanvas(cv) struct canvas *cv; { struct viewport *vp, *nvp; struct canvas **cvp; struct win *p; if (cv->c_slprev) cv->c_slprev->c_slnext = cv->c_slnext; if (cv->c_slnext) cv->c_slnext->c_slprev = cv->c_slprev; if (cv->c_slback && cv->c_slback->c_slperp == cv) cv->c_slback->c_slperp = cv->c_slnext ? cv->c_slnext : cv->c_slprev; if (cv->c_slperp) { while (cv->c_slperp) FreeCanvas(cv->c_slperp); LayerCleanupMemory(&cv->c_blank); free(cv); return; } if (display) { if (D_forecv == cv) D_forecv = 0; /* remove from canvas chain as SetCanvasWindow might call * some layer function */ for (cvp = &D_cvlist; *cvp ; cvp = &(*cvp)->c_next) if (*cvp == cv) { *cvp = cv->c_next; break; } } p = cv->c_layer ? Layer2Window(cv->c_layer) : 0; SetCanvasWindow(cv, 0); if (p) WindowChanged(p, 'u'); if (flayer == cv->c_layer) flayer = 0; for (vp = cv->c_vplist; vp; vp = nvp) { vp->v_canvas = 0; nvp = vp->v_next; vp->v_next = 0; free(vp); } evdeq(&cv->c_captev); LayerCleanupMemory(&cv->c_blank); free(cv); } int CountCanvas(cv) struct canvas *cv; { int num = 0; for (; cv; cv = cv->c_slnext) { if (cv->c_slperp) { struct canvas *cvp; int nump = 1, n; for (cvp = cv->c_slperp; cvp; cvp = cvp->c_slnext) if (cvp->c_slperp) { n = CountCanvas(cvp->c_slperp); if (n > nump) nump = n; } num += nump; } else num++; } return num; } int CountCanvasPerp(cv) struct canvas *cv; { struct canvas *cvp; int num = 1, n; for (cvp = cv->c_slperp; cvp; cvp = cvp->c_slnext) if (cvp->c_slperp) { n = CountCanvas(cvp->c_slperp); if (n > num) num = n; } return num; } struct canvas * FindCanvas(x, y) int x, y; { struct canvas *cv, *mcv = 0; int m, mm = 0; for (cv = D_cvlist; cv; cv = cv->c_next) { /* ye + 1 because of caption line */ if (x >= cv->c_xs && x <= cv->c_xe && y >= cv->c_ys && y <= cv->c_ye + 1) return cv; if (cv == D_forecv) continue; m = 0; if (x >= D_forecv->c_xs && x <= D_forecv->c_xe) { if (x < cv->c_xs || x > cv->c_xe) continue; if (y < D_forecv->c_ys && y < cv->c_ys) continue; if (y > D_forecv->c_ye + 1 && y > cv->c_ye + 1) continue; if (y < cv->c_ys) m = cv->c_ys - y; if (y > cv->c_ye + 1) m = y - (cv->c_ye + 1); } if (y >= D_forecv->c_ys && y <= D_forecv->c_ye + 1) { if (y < cv->c_ys || y > cv->c_ye + 1) continue; if (x < D_forecv->c_xs && x < cv->c_xs) continue; if (x > D_forecv->c_xe && x > cv->c_xe) continue; if (x < cv->c_xs) m = cv->c_xs - x; if (x > cv->c_xe) m = x - cv->c_xe; } if (m && (!mm || m < mm)) { mcv = cv; mm = m; } } return mcv ? mcv : D_forecv; } void SetCanvasWindow(cv, wi) struct canvas *cv; struct win *wi; { struct win *p = 0, **pp; struct layer *l; struct canvas *cvp, **cvpp; l = cv->c_layer; display = cv->c_display; if (l) { /* remove old layer */ for (cvpp = &l->l_cvlist; (cvp = *cvpp); cvpp = &cvp->c_lnext) if (cvp == cv) break; ASSERT(cvp); *cvpp = cvp->c_lnext; p = Layer2Window(l); l = cv->c_layer; cv->c_layer = 0; if (p && cv == D_forecv) { #ifdef MULTIUSER ReleaseAutoWritelock(display, p); #endif if (p->w_silence) { SetTimeout(&p->w_silenceev, p->w_silencewait * 1000); evenq(&p->w_silenceev); } D_other = fore; D_fore = 0; } if (l->l_cvlist == 0 && (p == 0 || l != p->w_savelayer)) KillLayerChain(l); } /* find right layer to display on canvas */ if (wi && wi->w_type != W_TYPE_GROUP) { l = &wi->w_layer; if (wi->w_savelayer && (wi->w_blocked || wi->w_savelayer->l_cvlist == 0)) l = wi->w_savelayer; } else { l = &cv->c_blank; if (wi) l->l_data = (char *)wi; else l->l_data = 0; } /* add our canvas to the layer's canvaslist */ ASSERT(l->l_cvlist != cv); cv->c_lnext = l->l_cvlist; l->l_cvlist = cv; cv->c_layer = l; cv->c_xoff = cv->c_xs; cv->c_yoff = cv->c_ys; RethinkViewportOffsets(cv); if (flayer == 0) flayer = l; if (wi && wi->w_type == W_TYPE_GROUP) { /* auto-start windowlist on groups */ struct display *d = display; struct layer *oldflayer = flayer; flayer = l; display_windows(0, 0, wi); flayer = oldflayer; display = d; } if (wi && D_other == wi) D_other = wi->w_next; /* Might be 0, but that's OK. */ if (cv == D_forecv) { D_fore = wi; fore = D_fore; /* XXX ? */ if (wi) { #ifdef MULTIUSER ObtainAutoWritelock(display, wi); #endif /* * Place the window at the head of the most-recently-used list */ if (windows != wi) { for (pp = &windows; (p = *pp); pp = &p->w_next) if (p == wi) break; ASSERT(p); *pp = p->w_next; p->w_next = windows; windows = p; WListLinkChanged(); } } } } static void cv_winid_fn(ev, data) struct event *ev; char *data; { int ox, oy; struct canvas *cv = (struct canvas *)data; display = cv->c_display; if (D_status == STATUS_ON_WIN) { SetTimeout(ev, 1); evenq(ev); return; } ox = D_x; oy = D_y; if (cv->c_ye + 1 < D_height) RefreshLine(cv->c_ye + 1, 0, D_width - 1, 0); if (ox != -1 && oy != -1) GotoPos(ox, oy); } int MakeDefaultCanvas() { struct canvas *cv; ASSERT(display); if ((cv = (struct canvas *)calloc(1, sizeof *cv)) == 0) return -1; cv->c_xs = 0; cv->c_xe = D_width - 1; cv->c_ys = (D_has_hstatus == HSTATUS_FIRSTLINE); cv->c_ye = D_height - 1 - (D_has_hstatus == HSTATUS_LASTLINE) - captionalways; debug2("MakeDefaultCanvas 0,0 %d,%d\n", cv->c_xe, cv->c_ye); cv->c_xoff = 0; cv->c_yoff = 0; cv->c_next = 0; cv->c_display = display; cv->c_vplist = 0; cv->c_slnext = 0; cv->c_slprev = 0; cv->c_slperp = 0; cv->c_slweight = 1; cv->c_slback = &D_canvas; D_canvas.c_slperp = cv; D_canvas.c_xs = cv->c_xs; D_canvas.c_xe = cv->c_xe; D_canvas.c_ys = cv->c_ys; D_canvas.c_ye = cv->c_ye; cv->c_slorient = SLICE_UNKN; cv->c_captev.type = EV_TIMEOUT; cv->c_captev.data = (char *)cv; cv->c_captev.handler = cv_winid_fn; CanvasInitBlank(cv); cv->c_lnext = 0; D_cvlist = cv; RethinkDisplayViewports(); D_forecv = cv; /* default input focus */ return 0; } static struct canvas ** CreateCanvasChainRec(cv, cvp) struct canvas *cv; struct canvas **cvp; { for (; cv; cv = cv->c_slnext) { if (cv->c_slperp) cvp = CreateCanvasChainRec(cv->c_slperp, cvp); else { *cvp = cv; cvp = &cv->c_next; } } return cvp; } void RecreateCanvasChain() { struct canvas **cvp; cvp = CreateCanvasChainRec(D_canvas.c_slperp, &D_cvlist); *cvp = 0; } void EqualizeCanvas(cv, gflag) struct canvas *cv; int gflag; { struct canvas *cv2; for (; cv; cv = cv->c_slnext) { if (cv->c_slperp && gflag) { cv->c_slweight = CountCanvasPerp(cv); for (cv2 = cv->c_slperp; cv2; cv2 = cv2->c_slnext) if (cv2->c_slperp) EqualizeCanvas(cv2->c_slperp, gflag); } else cv->c_slweight = 1; } } void ResizeCanvas(cv) struct canvas *cv; { struct canvas *cv2, *cvn, *fcv; int nh, i, maxi, hh, m, w, wsum; int need, got; int xs, ys, xe, ye; int focusmin = 0; xs = cv->c_xs; ys = cv->c_ys; xe = cv->c_xe; ye = cv->c_ye; cv = cv->c_slperp; debug2("ResizeCanvas: %d,%d", xs, ys); debug2(" %d,%d\n", xe, ye); if (cv == 0) return; if (cv->c_slorient == SLICE_UNKN) { ASSERT(!cv->c_slnext && !cv->c_slperp); cv->c_xs = xs; cv->c_xe = xe; cv->c_ys = ys; cv->c_ye = ye; cv->c_xoff = cv->c_xs; cv->c_yoff = cv->c_ys; cv->c_blank.l_width = cv->c_xe - cv->c_xs + 1; cv->c_blank.l_height = cv->c_ye - cv->c_ys + 1; return; } fcv = 0; if (focusminwidth || focusminheight) { debug("searching for focus canvas\n"); cv2 = D_forecv; while (cv2->c_slback) { if (cv2->c_slback == cv->c_slback) { fcv = cv2; focusmin = cv->c_slorient == SLICE_VERT ? focusminheight : focusminwidth; if (focusmin > 0) focusmin--; else if (focusmin < 0) focusmin = cv->c_slorient == SLICE_VERT ? ye - ys + 2 : xe - xs + 2; debug1("found, focusmin=%d\n", focusmin); } cv2 = cv2->c_slback; } } if (focusmin) { m = CountCanvas(cv) * 2; nh = cv->c_slorient == SLICE_VERT ? ye - ys + 2 : xe - xs + 2; nh -= m; if (nh < 0) nh = 0; if (focusmin > nh) focusmin = nh; debug1("corrected to %d\n", focusmin); } /* pass 1: calculate weight sum */ for (cv2 = cv, wsum = 0; cv2; cv2 = cv2->c_slnext) { debug1(" weight %d\n", cv2->c_slweight); wsum += cv2->c_slweight; } debug1("wsum = %d\n", wsum); if (wsum == 0) wsum = 1; w = wsum; /* pass 2: calculate need/excess space */ nh = cv->c_slorient == SLICE_VERT ? ye - ys + 2 : xe - xs + 2; for (cv2 = cv, need = got = 0; cv2; cv2 = cv2->c_slnext) { m = cv2->c_slperp ? CountCanvasPerp(cv2) * 2 - 1 : 1; if (cv2 == fcv) m += focusmin; hh = cv2->c_slweight ? nh * cv2->c_slweight / w : 0; w -= cv2->c_slweight; nh -= hh; debug2(" should %d min %d\n", hh, m); if (hh <= m + 1) need += m + 1 - hh; else got += hh - m - 1; } debug2("need: %d, got %d\n", need, got); if (need > got) need = got; /* pass 3: distribute space */ nh = cv->c_slorient == SLICE_VERT ? ye - ys + 2 : xe - xs + 2; i = cv->c_slorient == SLICE_VERT ? ys : xs; maxi = cv->c_slorient == SLICE_VERT ? ye : xe; w = wsum; for (; cv; cv = cvn) { cvn = cv->c_slnext; if (i > maxi) { if (cv->c_slprev && !cv->c_slback->c_slback && !cv->c_slprev->c_slperp && !cv->c_slprev->c_slprev) { cv->c_slprev->c_slorient = SLICE_UNKN; if (!captionalways) { cv->c_slback->c_ye++; cv->c_slprev->c_ye++; } } SetCanvasWindow(cv, 0); FreeCanvas(cv); continue; } m = cv->c_slperp ? CountCanvasPerp(cv) * 2 - 1 : 1; if (cv == fcv) m += focusmin; hh = cv->c_slweight ? nh * cv->c_slweight / w : 0; w -= cv->c_slweight; nh -= hh; debug2(" should %d min %d\n", hh, m); if (hh <= m + 1) { hh = m + 1; debug1(" -> %d\n", hh); } else { int hx = 1; //FIXME Division by zero (got) is posible. "hx = 1" is random number here!!! if (got != 0) hx = need * (hh - m - 1) / got; else debug(" got = 0\n"); debug3(" -> %d - %d = %d\n", hh, hx, hh - hx); got -= (hh - m - 1); hh -= hx; need -= hx; debug2(" now need=%d got=%d\n", need, got); } ASSERT(hh >= m + 1); /* hh is window size plus pation line */ if (i + hh > maxi + 2) { hh = maxi + 2 - i; debug1(" not enough space, reducing to %d\n", hh); } if (i + hh == maxi + 1) { hh++; debug(" incrementing as no other canvas will fit\n"); } if (cv->c_slorient == SLICE_VERT) { cv->c_xs = xs; cv->c_xe = xe; cv->c_ys = i; cv->c_ye = i + hh - 2; cv->c_xoff = xs; cv->c_yoff = i; } else { cv->c_xs = i; cv->c_xe = i + hh - 2; cv->c_ys = ys; cv->c_ye = ye; cv->c_xoff = i; cv->c_yoff = ys; } cv->c_xoff = cv->c_xs; cv->c_yoff = cv->c_ys; cv->c_blank.l_width = cv->c_xe - cv->c_xs + 1; cv->c_blank.l_height = cv->c_ye - cv->c_ys + 1; if (cv->c_slperp) { ResizeCanvas(cv); if (!cv->c_slperp->c_slnext) { debug("deleting perp node\n"); FreePerp(cv->c_slperp); FreePerp(cv); } } i += hh; } } static struct canvas * AddPerp(cv) struct canvas *cv; { struct canvas *pcv; debug("Creating new perp node\n"); if ((pcv = (struct canvas *)calloc(1, sizeof *cv)) == 0) return 0; pcv->c_next = 0; pcv->c_display = cv->c_display; pcv->c_slnext = cv->c_slnext; pcv->c_slprev = cv->c_slprev; pcv->c_slperp = cv; pcv->c_slback = cv->c_slback; if (cv->c_slback && cv->c_slback->c_slperp == cv) cv->c_slback->c_slperp = pcv; pcv->c_slorient = cv->c_slorient; pcv->c_xoff = 0; pcv->c_yoff = 0; pcv->c_xs = cv->c_xs; pcv->c_xe = cv->c_xe; pcv->c_ys = cv->c_ys; pcv->c_ye = cv->c_ye; if (pcv->c_slnext) pcv->c_slnext->c_slprev = pcv; if (pcv->c_slprev) pcv->c_slprev->c_slnext = pcv; pcv->c_slweight = cv->c_slweight; CanvasInitBlank(pcv); cv->c_slweight = 1; cv->c_slnext = 0; cv->c_slprev = 0; cv->c_slperp = 0; cv->c_slback = pcv; cv->c_slorient = SLICE_UNKN; return pcv; } int AddCanvas(orient) int orient; { struct canvas *cv; int xs, xe, ys, ye; int h, num; cv = D_forecv; debug2("AddCanvas orient %d, forecv is %d\n", orient, cv->c_slorient); if (cv->c_slorient != SLICE_UNKN && cv->c_slorient != orient) if (!AddPerp(cv)) return -1; cv = D_forecv; xs = cv->c_slback->c_xs; xe = cv->c_slback->c_xe; ys = cv->c_slback->c_ys; ye = cv->c_slback->c_ye; if (!captionalways && cv == D_canvas.c_slperp && !cv->c_slnext) ye--; /* need space for caption */ debug2("Adding Canvas to slice %d,%d ", xs, ys); debug2("%d,%d\n", xe, ye); num = CountCanvas(cv->c_slback->c_slperp) + 1; debug1("Num = %d\n", num); if (orient == SLICE_VERT) h = ye - ys + 1; else h = xe - xs + 1; h -= 2 * num - 1; if (h < 0) return -1; /* can't fit in */ if ((cv = (struct canvas *)calloc(1, sizeof *cv)) == 0) return -1; D_forecv->c_slback->c_ye = ye; /* in case we modified it above */ D_forecv->c_slorient = orient; /* in case it was UNKN */ cv->c_slnext = D_forecv->c_slnext; cv->c_slprev = D_forecv; D_forecv->c_slnext = cv; if (cv->c_slnext) cv->c_slnext->c_slprev = cv; cv->c_slorient = orient; cv->c_slback = D_forecv->c_slback; cv->c_xs = xs; cv->c_xe = xe; cv->c_ys = ys; cv->c_ye = ye; cv->c_xoff = 0; cv->c_yoff = 0; cv->c_display = display; cv->c_vplist = 0; cv->c_captev.type = EV_TIMEOUT; cv->c_captev.data = (char *)cv; cv->c_captev.handler = cv_winid_fn; CanvasInitBlank(cv); cv->c_lnext = 0; cv->c_next = 0; cv = cv->c_slback; EqualizeCanvas(cv->c_slperp, 0); ResizeCanvas(cv); RecreateCanvasChain(); RethinkDisplayViewports(); ResizeLayersToCanvases(); return 0; } void RemCanvas() { int ye; struct canvas *cv; debug("RemCanvas\n"); cv = D_forecv; ye = cv->c_slback->c_ye; if (cv->c_slorient == SLICE_UNKN) return; while (cv->c_slprev) cv = cv->c_slprev; if (!cv->c_slnext) return; if (!cv->c_slnext->c_slnext && cv->c_slback->c_slback) { /* two canvases in slice, kill perp node */ cv = D_forecv; debug("deleting perp node\n"); FreePerp(cv->c_slprev ? cv->c_slprev : cv->c_slnext); FreePerp(cv->c_slback); } /* free canvas */ cv = D_forecv; D_forecv = cv->c_slprev; if (!D_forecv) D_forecv = cv->c_slnext; FreeCanvas(cv); cv = D_forecv; while (D_forecv->c_slperp) D_forecv = D_forecv->c_slperp; /* if only one canvas left, set orient back to unknown */ if (!cv->c_slnext && !cv->c_slprev && !cv->c_slback->c_slback && !cv->c_slperp) { cv->c_slorient = SLICE_UNKN; if (!captionalways) cv->c_slback->c_ye = ++ye; /* caption line no longer needed */ } cv = cv->c_slback; EqualizeCanvas(cv->c_slperp, 0); ResizeCanvas(cv); D_fore = Layer2Window(D_forecv->c_layer); flayer = D_forecv->c_layer; RecreateCanvasChain(); RethinkDisplayViewports(); ResizeLayersToCanvases(); } void OneCanvas() { struct canvas *cv = D_forecv, *ocv = 0; if (cv->c_slprev) { ocv = cv->c_slprev; cv->c_slprev->c_slnext = cv->c_slnext; } if (cv->c_slnext) { ocv = cv->c_slnext; cv->c_slnext->c_slprev = cv->c_slprev; } if (!ocv) return; if (cv->c_slback && cv->c_slback->c_slperp == cv) cv->c_slback->c_slperp = ocv; cv->c_slorient = SLICE_UNKN; while (D_canvas.c_slperp) FreeCanvas(D_canvas.c_slperp); cv = D_forecv; D_canvas.c_slperp = cv; cv->c_slback = &D_canvas; cv->c_slnext = 0; cv->c_slprev = 0; ASSERT(!cv->c_slperp); if (!captionalways) D_canvas.c_ye++; /* caption line no longer needed */ ResizeCanvas(&D_canvas); RecreateCanvasChain(); RethinkDisplayViewports(); ResizeLayersToCanvases(); } void DupLayoutCv(cvf, cvt, save) struct canvas *cvf, *cvt; int save; { while(cvf) { cvt->c_slorient = cvf->c_slorient; cvt->c_slweight = cvf->c_slweight; if (cvf == D_forecv) D_forecv = cvt; if (!save) { cvt->c_display = display; if (!cvf->c_slperp) { cvt->c_captev.type = EV_TIMEOUT; cvt->c_captev.data = (char *)cvt; cvt->c_captev.handler = cv_winid_fn; cvt->c_blank.l_cvlist = 0; cvt->c_blank.l_layfn = &BlankLf; cvt->c_blank.l_bottom = &cvt->c_blank; } cvt->c_layer = cvf->c_layer; } else { struct win *p = cvf->c_layer ? Layer2Window(cvf->c_layer) : 0; cvt->c_layer = p ? &p->w_layer : 0; } if (cvf->c_slperp) { cvt->c_slperp = (struct canvas *)calloc(1, sizeof(struct canvas)); cvt->c_slperp->c_slback = cvt; CanvasInitBlank(cvt->c_slperp); DupLayoutCv(cvf->c_slperp, cvt->c_slperp, save); } if (cvf->c_slnext) { cvt->c_slnext = (struct canvas *)calloc(1, sizeof(struct canvas)); cvt->c_slnext->c_slprev = cvt; cvt->c_slnext->c_slback = cvt->c_slback; CanvasInitBlank(cvt->c_slnext); } cvf = cvf->c_slnext; cvt = cvt->c_slnext; } } void PutWindowCv(cv) struct canvas *cv; { struct win *p; for (; cv; cv = cv->c_slnext) { if (cv->c_slperp) { PutWindowCv(cv->c_slperp); continue; } p = cv->c_layer ? (struct win *)cv->c_layer->l_data : 0; cv->c_layer = 0; SetCanvasWindow(cv, p); } }