1247 lines
29 KiB
C
1247 lines
29 KiB
C
/* Copyright (c) 2008-2017
|
|
* 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 <sys/types.h>
|
|
|
|
#include "config.h"
|
|
#include "screen.h"
|
|
#include "mark.h"
|
|
#include "extern.h"
|
|
#include "braille.h"
|
|
|
|
extern struct display *display, *displays;
|
|
|
|
extern struct mline mline_blank, mline_null;
|
|
extern struct mchar mchar_blank, mchar_null;
|
|
|
|
extern struct layer *flayer; /* sigh */
|
|
extern struct LayFuncs WinLf;
|
|
extern struct LayFuncs BlankLf;
|
|
|
|
|
|
static struct mline *mloff __P((struct mline *, int));
|
|
|
|
/*
|
|
* Layer subsystem.
|
|
*
|
|
* ...here is all the clipping code... beware!
|
|
*
|
|
* XXX: add some speedup code!
|
|
*
|
|
*/
|
|
|
|
static struct mline *mloff(struct mline *ml, int off)
|
|
{
|
|
static struct mline mml;
|
|
if (ml == 0)
|
|
return 0;
|
|
|
|
mml.image = ml->image + off;
|
|
mml.attr = ml->attr + off;
|
|
#ifdef FONT
|
|
mml.font = ml->font + off;
|
|
mml.fontx = ml->fontx + off;
|
|
#endif
|
|
|
|
#ifdef COLOR
|
|
mml.color = ml->color + off;
|
|
# ifdef COLORS256
|
|
mml.colorx = ml->colorx + off;
|
|
# endif
|
|
#endif
|
|
|
|
return &mml;
|
|
}
|
|
|
|
#ifdef UTF8
|
|
# define RECODE_MCHAR(mc) ((l->l_encoding == UTF8) != (D_encoding == UTF8) ? recode_mchar(mc, l->l_encoding, D_encoding) : (mc))
|
|
# define RECODE_MLINE(ml) ((l->l_encoding == UTF8) != (D_encoding == UTF8) ? recode_mline(ml, l->l_width, l->l_encoding, D_encoding) : (ml))
|
|
#else
|
|
# define RECODE_MCHAR(mc) (mc)
|
|
# define RECODE_MLINE(ml) (ml)
|
|
#endif
|
|
|
|
#define FOR_EACH_UNPAUSED_CANVAS(l, fn) for (cv = (l)->l_cvlist; cv; cv = cv->c_lnext) \
|
|
{ \
|
|
if ((l)->l_pause.d && cv->c_slorient) \
|
|
continue; \
|
|
fn \
|
|
}
|
|
|
|
void LGotoPos(struct layer *l, int x, int y)
|
|
{
|
|
struct canvas *cv;
|
|
struct viewport *vp;
|
|
int x2, y2;
|
|
|
|
if (l->l_pause.d)
|
|
LayPauseUpdateRegion(l, x, x, y, y);
|
|
|
|
#ifdef HAVE_BRAILLE
|
|
if (bd.bd_refreshing)
|
|
return;
|
|
#endif
|
|
FOR_EACH_UNPAUSED_CANVAS(l,
|
|
{
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
if (cv != D_forecv)
|
|
continue;
|
|
x2 = x + cv->c_xoff;
|
|
y2 = y + cv->c_yoff;
|
|
debug2("---LGotoPos %d %d\n", x2, y2);
|
|
if (x2 < cv->c_xs)
|
|
x2 = cv->c_xs;
|
|
if (y2 < cv->c_ys)
|
|
y2 = cv->c_ys;
|
|
if (x2 > cv->c_xe)
|
|
x2 = cv->c_xe;
|
|
if (y2 > cv->c_ye)
|
|
y2 = cv->c_ye;
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next) {
|
|
if (x2 < vp->v_xs || x2 > vp->v_xe)
|
|
continue;
|
|
if (y2 < vp->v_ys || y2 > vp->v_ye)
|
|
continue;
|
|
GotoPos(x2, y2);
|
|
break;
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
void LScrollH(struct layer *l, int n, int y, int xs, int xe, int bce, struct mline *ol)
|
|
{
|
|
struct canvas *cv;
|
|
struct viewport *vp;
|
|
int y2, xs2, xe2;
|
|
|
|
if (n == 0)
|
|
return;
|
|
if (l->l_pause.d)
|
|
LayPauseUpdateRegion(l, xs, xe, y, y);
|
|
|
|
FOR_EACH_UNPAUSED_CANVAS(l,
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next) {
|
|
y2 = y + vp->v_yoff;
|
|
if (y2 < vp->v_ys || y2 > vp->v_ye)
|
|
continue;
|
|
xs2 = xs + vp->v_xoff;
|
|
xe2 = xe + vp->v_xoff;
|
|
if (xs2 < vp->v_xs)
|
|
xs2 = vp->v_xs;
|
|
if (xe2 > vp->v_xe)
|
|
xe2 = vp->v_xe;
|
|
if (xs2 > xe2)
|
|
continue;
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
ScrollH(y2, xs2, xe2, n, bce, ol ? mloff(ol, -vp->v_xoff) : 0);
|
|
if (xe2 - xs2 == xe - xs)
|
|
continue;
|
|
if (n > 0) {
|
|
xs2 = xe2 + 1 - n;
|
|
xe2 = xe + vp->v_xoff - n;
|
|
}
|
|
else {
|
|
xe2 = xs2 - 1 - n;
|
|
xs2 = xs + vp->v_xoff - n;
|
|
}
|
|
|
|
if (xs2 < vp->v_xs)
|
|
xs2 = vp->v_xs;
|
|
if (xe2 > vp->v_xe)
|
|
xe2 = vp->v_xe;
|
|
if (xs2 <= xe2)
|
|
RefreshArea(xs2, y2, xe2, y2, 1);
|
|
}
|
|
);
|
|
}
|
|
|
|
void LScrollV(struct layer *l, int n, int ys, int ye, int bce)
|
|
{
|
|
struct canvas *cv;
|
|
struct viewport *vp;
|
|
int ys2, ye2, xs2, xe2;
|
|
if (n == 0)
|
|
return;
|
|
if (l->l_pause.d)
|
|
LayPauseUpdateRegion(l, 0, l->l_width - 1, ys, ye);
|
|
FOR_EACH_UNPAUSED_CANVAS(l,
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next) {
|
|
xs2 = vp->v_xoff;
|
|
xe2 = l->l_width - 1 + vp->v_xoff;
|
|
ys2 = ys + vp->v_yoff;
|
|
ye2 = ye + vp->v_yoff;
|
|
|
|
if (xs2 < vp->v_xs)
|
|
xs2 = vp->v_xs;
|
|
if (xe2 > vp->v_xe)
|
|
xe2 = vp->v_xe;
|
|
if (ys2 < vp->v_ys)
|
|
ys2 = vp->v_ys;
|
|
if (ye2 > vp->v_ye)
|
|
ye2 = vp->v_ye;
|
|
if (ys2 > ye2 || xs2 > xe2)
|
|
continue;
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
#if 0
|
|
ScrollV(xs2, ys2, xe2, ye2, n, bce);
|
|
#else
|
|
ScrollV(vp->v_xs, ys2, vp->v_xe, ye2, n, bce);
|
|
#endif
|
|
|
|
debug2("LScrollV: %d %d", ys, ye);
|
|
debug2(" -> %d %d\n", ys2, ye2);
|
|
if (ye2 - ys2 == ye - ys)
|
|
continue;
|
|
if (n > 0) {
|
|
ys2 = ye2 + 1 - n;
|
|
ye2 = ye + vp->v_yoff - n;
|
|
}
|
|
else {
|
|
ye2 = ys2 - 1 - n;
|
|
ys2 = ys + vp->v_yoff - n;
|
|
}
|
|
|
|
debug2("LScrollV: - %d %d\n", ys2, ye2);
|
|
if (ys2 < vp->v_ys)
|
|
ys2 = vp->v_ys;
|
|
if (ye2 > vp->v_ye)
|
|
ye2 = vp->v_ye;
|
|
debug2("LScrollV: - %d %d\n", ys2, ye2);
|
|
if (ys2 <= ye2)
|
|
RefreshArea(xs2, ys2, xe2, ye2, 1);
|
|
}
|
|
);
|
|
}
|
|
|
|
void LInsChar(struct layer *l, struct mchar *c, int x, int y, struct mline *ol)
|
|
{
|
|
struct canvas *cv;
|
|
struct viewport *vp;
|
|
int xs2, xe2, y2, f;
|
|
struct mchar *c2, cc;
|
|
struct mline *rol;
|
|
|
|
if (l->l_pause.d)
|
|
LayPauseUpdateRegion(l, x, l->l_width - 1, y, y);
|
|
FOR_EACH_UNPAUSED_CANVAS(l,
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next) {
|
|
y2 = y + vp->v_yoff;
|
|
if (y2 < vp->v_ys || y2 > vp->v_ye)
|
|
continue;
|
|
xs2 = x + vp->v_xoff;
|
|
xe2 = l->l_width - 1 + vp->v_xoff;
|
|
c2 = c;
|
|
f = 0;
|
|
if (xs2 < vp->v_xs) {
|
|
xs2 = vp->v_xs;
|
|
c2 = &mchar_blank;
|
|
if (ol) {
|
|
int i = xs2 - vp->v_xoff - 1;
|
|
if (i >= 0 && i < l->l_width) {
|
|
copy_mline2mchar(&cc, ol, i);
|
|
c2 = &cc;
|
|
}
|
|
}
|
|
else
|
|
f = 1;
|
|
}
|
|
|
|
if (xe2 > vp->v_xe)
|
|
xe2 = vp->v_xe;
|
|
if (xs2 > xe2)
|
|
continue;
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
rol = RECODE_MLINE(ol);
|
|
InsChar(RECODE_MCHAR(c2), xs2, xe2, y2, mloff(rol, -vp->v_xoff));
|
|
if (f)
|
|
RefreshArea(xs2, y2, xs2, y2, 1);
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
void LPutChar(struct layer *l, struct mchar *c, int x, int y)
|
|
{
|
|
struct canvas *cv;
|
|
struct viewport *vp;
|
|
int x2, y2;
|
|
|
|
#ifdef HAVE_BRAILLE
|
|
if (bd.bd_refreshing) {
|
|
BPutChar(l, c, x, y);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (l->l_pause.d)
|
|
LayPauseUpdateRegion(l, x,
|
|
|
|
#ifdef DW_CHARS
|
|
x + (c->mbcs ? 1 : 0)
|
|
#else
|
|
x
|
|
#endif
|
|
, y, y);
|
|
|
|
|
|
FOR_EACH_UNPAUSED_CANVAS(l,
|
|
{
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next) {
|
|
y2 = y + vp->v_yoff;
|
|
if (y2 < vp->v_ys || y2 > vp->v_ye)
|
|
continue;
|
|
x2 = x + vp->v_xoff;
|
|
if (x2 < vp->v_xs || x2 > vp->v_xe)
|
|
continue;
|
|
PutChar(RECODE_MCHAR(c), x2, y2);
|
|
break;
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
void LPutStr(struct layer *l, char *s, int n, struct mchar *r, int x, int y)
|
|
{
|
|
struct canvas *cv;
|
|
struct viewport *vp;
|
|
char *s2;
|
|
int xs2, xe2, y2;
|
|
|
|
if (x + n > l->l_width)
|
|
n = l->l_width - x;
|
|
|
|
#ifdef HAVE_BRAILLE
|
|
if (bd.bd_refreshing) {
|
|
BPutStr(l, s, n, r, x, y);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (l->l_pause.d)
|
|
LayPauseUpdateRegion(l, x, x + n - 1, y, y);
|
|
|
|
FOR_EACH_UNPAUSED_CANVAS(l,
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next) {
|
|
y2 = y + vp->v_yoff;
|
|
if (y2 < vp->v_ys || y2 > vp->v_ye)
|
|
continue;
|
|
xs2 = x + vp->v_xoff;
|
|
xe2 = xs2 + n - 1;
|
|
if (xs2 < vp->v_xs)
|
|
xs2 = vp->v_xs;
|
|
if (xe2 > vp->v_xe)
|
|
xe2 = vp->v_xe;
|
|
if (xs2 > xe2)
|
|
continue;
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
GotoPos(xs2, y2);
|
|
SetRendition(r);
|
|
s2 = s + xs2 - x - vp->v_xoff;
|
|
|
|
#ifdef UTF8
|
|
if (D_encoding == UTF8 && l->l_encoding != UTF8 && (r->font || r->fontx || l->l_encoding)) {
|
|
struct mchar mc;
|
|
mc = *r;
|
|
while (xs2 <= xe2) {
|
|
mc.image = *s2++;
|
|
PutChar(RECODE_MCHAR(&mc), xs2++, y2);
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
while (xs2++ <= xe2)
|
|
PUTCHARLP(*s2++);
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
void LPutWinMsg(struct layer *l, char *s, int n, struct mchar *r, int x, int y)
|
|
{
|
|
struct canvas *cv;
|
|
struct viewport *vp;
|
|
int xs2, xe2, y2, len, len2;
|
|
struct mchar or;
|
|
|
|
if (x + n > l->l_width)
|
|
n = l->l_width - x;
|
|
|
|
#ifdef HAVE_BRAILLE
|
|
if (bd.bd_refreshing) {
|
|
BPutStr(l, s, n, r, x, y);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (l->l_pause.d)
|
|
LayPauseUpdateRegion(l, x, x + n - 1, y, y);
|
|
|
|
len = strlen(s);
|
|
if (len > n)
|
|
len = n;
|
|
|
|
FOR_EACH_UNPAUSED_CANVAS(l,
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next) {
|
|
y2 = y + vp->v_yoff;
|
|
if (y2 < vp->v_ys || y2 > vp->v_ye)
|
|
continue;
|
|
xs2 = x + vp->v_xoff;
|
|
xe2 = xs2 + n - 1;
|
|
if (xs2 < vp->v_xs)
|
|
xs2 = vp->v_xs;
|
|
if (xe2 > vp->v_xe)
|
|
xe2 = vp->v_xe;
|
|
if (xs2 > xe2)
|
|
continue;
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
GotoPos(xs2, y2);
|
|
SetRendition(r);
|
|
len2 = xe2 - (x + vp->v_xoff) + 1;
|
|
if (len2 > len)
|
|
len2 = len;
|
|
PutWinMsg(s, xs2 - x - vp->v_xoff, len2);
|
|
xs2 = x + vp->v_xoff + len2;
|
|
if (xs2 < vp->v_xs)
|
|
xs2 = vp->v_xs;
|
|
or = D_rend;
|
|
GotoPos(xs2, y2);
|
|
SetRendition(&or);
|
|
while (xs2++ <= xe2)
|
|
PUTCHARLP(' ');
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
void LClearLine(struct layer *l, int y, int xs, int xe, int bce, struct mline *ol)
|
|
{
|
|
struct canvas *cv;
|
|
struct viewport *vp;
|
|
int y2, xs2, xe2;
|
|
|
|
/* check for magic margin condition */
|
|
if (xs >= l->l_width)
|
|
xs = l->l_width - 1;
|
|
if (xe >= l->l_width)
|
|
xe = l->l_width - 1;
|
|
if (l->l_pause.d)
|
|
LayPauseUpdateRegion(l, xs, xe, y, y);
|
|
|
|
FOR_EACH_UNPAUSED_CANVAS(l,
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next) {
|
|
xs2 = xs + vp->v_xoff;
|
|
xe2 = xe + vp->v_xoff;
|
|
y2 = y + vp->v_yoff;
|
|
if (y2 < vp->v_ys || y2 > vp->v_ye)
|
|
continue;
|
|
if (xs2 < vp->v_xs)
|
|
xs2 = vp->v_xs;
|
|
if (xe2 > vp->v_xe)
|
|
xe2 = vp->v_xe;
|
|
if (xs2 > xe2)
|
|
continue;
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
ClearLine(ol ? mloff(RECODE_MLINE(ol), -vp->v_xoff) : (struct mline *)0, y2, xs2, xe2, bce);
|
|
}
|
|
);
|
|
}
|
|
|
|
void LClearArea(struct layer *l, int xs, int ys, int xe, int ye, int bce, int uself)
|
|
{
|
|
struct canvas *cv;
|
|
struct viewport *vp;
|
|
int xs2, ys2, xe2, ye2;
|
|
|
|
#ifdef HAVE_BRAILLE
|
|
if (bd.bd_refreshing)
|
|
return;
|
|
#endif
|
|
|
|
/* Check for zero-height window */
|
|
if (ys < 0 || ye < ys)
|
|
return;
|
|
|
|
/* check for magic margin condition */
|
|
if (xs >= l->l_width)
|
|
xs = l->l_width - 1;
|
|
if (xe >= l->l_width)
|
|
xe = l->l_width - 1;
|
|
if (l->l_pause.d)
|
|
LayPauseUpdateRegion(l, xs, xe, ys, ye);
|
|
|
|
FOR_EACH_UNPAUSED_CANVAS(l,
|
|
{
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next) {
|
|
xs2 = xs + vp->v_xoff;
|
|
xe2 = xe + vp->v_xoff;
|
|
ys2 = ys + vp->v_yoff;
|
|
ye2 = ye + vp->v_yoff;
|
|
|
|
if (xs2 < vp->v_xs)
|
|
xs2 = vp->v_xs;
|
|
if (xe2 > vp->v_xe)
|
|
xe2 = vp->v_xe;
|
|
if (xs2 > vp->v_xe)
|
|
ys2++;
|
|
if (xe2 < vp->v_xs)
|
|
ye2--;
|
|
if (ys2 < vp->v_ys)
|
|
ys2 = vp->v_ys;
|
|
if (ye2 > vp->v_ye)
|
|
ye2 = vp->v_ye;
|
|
if (ys2 > ye2)
|
|
continue;
|
|
#if 0
|
|
xcs = vp->v_xoff;
|
|
xce = l->l_width - 1 + vp->v_xoff;
|
|
if (xcs < vp->v_xs)
|
|
xcs = vp->v_xs;
|
|
if (xce > vp->v_xe)
|
|
xce = vp->v_xe;
|
|
if (xcs > xce)
|
|
continue;
|
|
if (ys2 != ys + vp->v_yoff)
|
|
xs2 = xcs;
|
|
if (ye2 != ye + vp->v_yoff)
|
|
xe2 = xce;
|
|
display = cv->c_display;
|
|
ClearArea(xs2, ys2, xcs, xce, xe2, ye2, bce, uself);
|
|
#else
|
|
|
|
if (xs == 0 || ys2 != ys + vp->v_yoff)
|
|
xs2 = vp->v_xs;
|
|
if (xe == l->l_width - 1 || ye2 != ye + vp->v_yoff)
|
|
xe2 = vp->v_xe;
|
|
display = cv->c_display;
|
|
ClearArea(xs2, ys2, vp->v_xs, vp->v_xe, xe2, ye2, bce, uself);
|
|
|
|
if (xe == l->l_width - 1 && xe2 > vp->v_xoff + xe) {
|
|
int y;
|
|
SetRendition(&mchar_blank);
|
|
for (y = ys2; y <= ye2; y++) {
|
|
GotoPos(xe + vp->v_xoff + 1, y);
|
|
PUTCHARLP('|');
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
void LCDisplayLine(struct layer *l, struct mline *ml, int y, int xs, int xe, int isblank)
|
|
{
|
|
struct canvas *cv;
|
|
struct viewport *vp;
|
|
int xs2, xe2, y2;
|
|
|
|
#ifdef HAVE_BRAILLE
|
|
if (bd.bd_refreshing) {
|
|
BCDisplayLine(l, ml, y, xs, xe, isblank);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (l->l_pause.d)
|
|
LayPauseUpdateRegion(l, xs, xe, y, y);
|
|
|
|
FOR_EACH_UNPAUSED_CANVAS(l,
|
|
{
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next) {
|
|
xs2 = xs + vp->v_xoff;
|
|
xe2 = xe + vp->v_xoff;
|
|
y2 = y + vp->v_yoff;
|
|
if (y2 < vp->v_ys || y2 > vp->v_ye)
|
|
continue;
|
|
if (xs2 < vp->v_xs)
|
|
xs2 = vp->v_xs;
|
|
if (xe2 > vp->v_xe)
|
|
xe2 = vp->v_xe;
|
|
if (xs2 > xe2)
|
|
continue;
|
|
display = cv->c_display;
|
|
debug3("LCDisplayLine: DisplayLine %d, %d-%d", y2, xs2, xe2);
|
|
debug1(" mloff = %d\n", -vp->v_xoff);
|
|
DisplayLine(isblank ? &mline_blank : &mline_null, mloff(RECODE_MLINE(ml), -vp->v_xoff), y2, xs2, xe2);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
void LCDisplayLineWrap(struct layer *l, struct mline *ml, int y, int from, int to, int isblank)
|
|
{
|
|
struct mchar nc;
|
|
copy_mline2mchar(&nc, ml, 0);
|
|
|
|
#ifdef DW_CHARS
|
|
if (dw_left(ml, 0, l->l_encoding)) {
|
|
nc.mbcs = ml->image[1];
|
|
from++;
|
|
}
|
|
#endif
|
|
|
|
LWrapChar(l, &nc, y - 1, -1, -1, 0);
|
|
from++;
|
|
if (from <= to)
|
|
LCDisplayLine(l, ml, y, from, to, isblank);
|
|
}
|
|
|
|
|
|
void LSetRendition(struct layer *l, struct mchar *r)
|
|
{
|
|
struct canvas *cv;
|
|
|
|
for (cv = l->l_cvlist; cv; cv = cv->c_lnext) {
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
SetRendition(r);
|
|
}
|
|
}
|
|
|
|
|
|
void LWrapChar(struct layer *l, struct mchar *c, int y, int top, int bot, int ins)
|
|
{
|
|
struct canvas *cv, *cvlist, *cvlnext;
|
|
struct viewport *vp, *evp, **vpp;
|
|
int yy, y2, yy2, top2, bot2;
|
|
int bce;
|
|
|
|
if (l->l_pause.d)
|
|
/* XXX: 'y'? */
|
|
LayPauseUpdateRegion(l, 0, l->l_width - 1, top, bot);
|
|
|
|
#ifdef COLOR
|
|
bce = rend_getbg(c);
|
|
#else
|
|
bce = 0;
|
|
#endif
|
|
|
|
if (y != bot) {
|
|
/* simple case: no scrolling */
|
|
/* cursor after wrapping */
|
|
yy = y == l->l_height - 1 ? y : y + 1;
|
|
|
|
FOR_EACH_UNPAUSED_CANVAS(l,
|
|
{
|
|
y2 = 0; /* gcc -Wall */
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
|
|
/* find the viewport of the wrapped character */
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next) {
|
|
y2 = y + vp->v_yoff;
|
|
yy2 = yy + vp->v_yoff;
|
|
if (yy2 >= vp->v_ys && yy2 <= vp->v_ye && vp->v_xoff >= vp->v_xs && vp->v_xoff <= vp->v_xe)
|
|
break;
|
|
}
|
|
|
|
if (vp == 0)
|
|
continue; /* nothing to do, character not visible */
|
|
|
|
/* find the viewport of the character at the end of the line*/
|
|
for (evp = cv->c_vplist; evp; evp = evp->v_next)
|
|
if (y2 >= evp->v_ys && y2 <= evp->v_ye && evp->v_xoff + l->l_width - 1 >= evp->v_xs && evp->v_xoff + l->l_width - 1 <= evp->v_xe)
|
|
break; /* gotcha! */
|
|
|
|
if (evp == 0 || (ins && vp->v_xoff + l->l_width - 1 > vp->v_ye)) {
|
|
/* no wrapping possible */
|
|
debug("LWrap: can't wrap!\n");
|
|
cvlist = l->l_cvlist;
|
|
cvlnext = cv->c_lnext;
|
|
l->l_cvlist = cv;
|
|
cv->c_lnext = 0;
|
|
|
|
if (ins)
|
|
LInsChar(l, c, 0, yy, 0);
|
|
else
|
|
LPutChar(l, c, 0, yy);
|
|
|
|
l->l_cvlist = cvlist;
|
|
cv->c_lnext = cvlnext;
|
|
}
|
|
else
|
|
WrapChar(RECODE_MCHAR(c), vp->v_xoff + l->l_width, y2, vp->v_xoff, -1, vp->v_xoff + l->l_width - 1, -1, ins);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
/* hard case: scroll up*/
|
|
FOR_EACH_UNPAUSED_CANVAS(l,
|
|
{
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
|
|
/* search for wrap viewport */
|
|
for (vpp = &cv->c_vplist; (vp = *vpp); vpp = &vp->v_next) {
|
|
yy2 = bot + vp->v_yoff;
|
|
if (yy2 >= vp->v_ys && yy2 <= vp->v_ye && vp->v_xoff >= vp->v_xs && vp->v_xoff + l->l_width - 1 <= vp->v_xe)
|
|
break;
|
|
}
|
|
|
|
if (vp) {
|
|
/* great, can use Wrap on the vp */
|
|
/* temporarily remove vp from cvlist */
|
|
*vpp = vp->v_next;
|
|
}
|
|
|
|
if (cv->c_vplist) {
|
|
/* scroll all viewports != vp */
|
|
cvlist = l->l_cvlist;
|
|
cvlnext = cv->c_lnext;
|
|
l->l_cvlist = cv;
|
|
cv->c_lnext = 0;
|
|
LScrollV(l, 1, top, bot, bce);
|
|
if (!vp) {
|
|
if (ins)
|
|
LInsChar(l, c, 0, bot, 0);
|
|
else
|
|
LPutChar(l, c, 0, bot);
|
|
}
|
|
|
|
l->l_cvlist = cvlist;
|
|
cv->c_lnext = cvlnext;
|
|
}
|
|
if (vp) {
|
|
/* add vp back to cvlist */
|
|
*vpp = vp;
|
|
top2 = top + vp->v_yoff;
|
|
bot2 = bot + vp->v_yoff;
|
|
if (top2 < vp->v_ys)
|
|
top2 = vp->v_ys;
|
|
WrapChar(RECODE_MCHAR(c), vp->v_xoff + l->l_width, bot2, vp->v_xoff, top2, vp->v_xoff + l->l_width - 1, bot2, ins);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
void LCursorVisibility(struct layer *l, int vis)
|
|
{
|
|
struct canvas *cv;
|
|
for (cv = l->l_cvlist; cv; cv = cv->c_lnext) {
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
if (cv != D_forecv)
|
|
continue;
|
|
CursorVisibility(vis);
|
|
}
|
|
}
|
|
|
|
|
|
void LSetFlow(struct layer *l, int flow)
|
|
{
|
|
struct canvas *cv;
|
|
for (cv = l->l_cvlist; cv; cv = cv->c_lnext) {
|
|
display = cv->c_display;
|
|
if (cv != D_forecv)
|
|
continue;
|
|
SetFlow(flow);
|
|
}
|
|
}
|
|
|
|
|
|
void LKeypadMode(struct layer *l, int on)
|
|
{
|
|
struct canvas *cv;
|
|
for (cv = l->l_cvlist; cv; cv = cv->c_lnext) {
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
if (cv != D_forecv)
|
|
continue;
|
|
KeypadMode(on);
|
|
}
|
|
}
|
|
|
|
|
|
void LCursorkeysMode(struct layer *l, int on)
|
|
{
|
|
struct canvas *cv;
|
|
for (cv = l->l_cvlist; cv; cv = cv->c_lnext) {
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
if (cv != D_forecv)
|
|
continue;
|
|
CursorkeysMode(on);
|
|
}
|
|
}
|
|
|
|
|
|
void LMouseMode(struct layer *l, int on)
|
|
{
|
|
struct canvas *cv;
|
|
for (cv = l->l_cvlist; cv; cv = cv->c_lnext) {
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
if (cv != D_forecv)
|
|
continue;
|
|
MouseMode(on);
|
|
}
|
|
}
|
|
|
|
void LClearAll(struct layer *l, int uself)
|
|
{
|
|
LClearArea(l, 0, 0, l->l_width - 1, l->l_height - 1, 0, uself);
|
|
}
|
|
|
|
|
|
void LRefreshAll(struct layer *l, int isblank)
|
|
{
|
|
struct layer *oldflayer;
|
|
int y;
|
|
|
|
debug1("LRefreshAll isblank=%d\n", isblank);
|
|
oldflayer = flayer;
|
|
flayer = l;
|
|
if (!isblank)
|
|
LClearArea(l, 0, 0, l->l_width - 1, l->l_height - 1, 0, 0);
|
|
/* signal full refresh */
|
|
LayRedisplayLine(-1, -1, -1, 1);
|
|
for (y = 0; y < l->l_height; y++)
|
|
LayRedisplayLine(y, 0, l->l_width - 1, 1);
|
|
flayer = oldflayer;
|
|
}
|
|
|
|
void
|
|
/*VARARGS2*/
|
|
#if defined(USEVARARGS) && defined(__STDC__)
|
|
LMsg(int err, const char *fmt, VA_DOTS)
|
|
#else
|
|
LMsg(err, fmt, VA_DOTS)
|
|
int err;
|
|
const char *fmt;
|
|
VA_DECL
|
|
#endif
|
|
{
|
|
VA_LIST(ap)
|
|
char buf[MAXPATHLEN*2];
|
|
char *p = buf;
|
|
struct canvas *cv;
|
|
|
|
VA_START(ap, fmt);
|
|
fmt = DoNLS(fmt);
|
|
(void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
|
|
VA_END(ap);
|
|
|
|
if (err) {
|
|
p += strlen(p);
|
|
*p++ = ':';
|
|
*p++ = ' ';
|
|
strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
|
|
buf[sizeof(buf) - 1] = 0;
|
|
}
|
|
|
|
debug2("LMsg('%s') (%#lx);\n", buf, (long)flayer);
|
|
for (display = displays; display; display = display->d_next) {
|
|
for (cv = D_cvlist; cv; cv = cv->c_next)
|
|
if (cv->c_layer == flayer)
|
|
break;
|
|
|
|
if (cv == 0)
|
|
continue;
|
|
MakeStatus(buf);
|
|
}
|
|
}
|
|
|
|
void
|
|
LExtMouseMode(l, on)
|
|
struct layer *l;
|
|
int on;
|
|
{
|
|
struct canvas *cv;
|
|
for (cv = l->l_cvlist; cv; cv = cv->c_lnext)
|
|
{
|
|
display = cv->c_display;
|
|
if (D_blocked)
|
|
continue;
|
|
if (cv != D_forecv)
|
|
continue;
|
|
ExtMouseMode(on);
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
|
|
/*
|
|
* Layer creation / removal
|
|
*/
|
|
|
|
void KillLayerChain(struct layer *lay)
|
|
{
|
|
struct canvas *cv, *ncv;
|
|
struct layer *l, *oldflayer;
|
|
|
|
oldflayer = flayer;
|
|
debug1("KillLayerChain %#lx\n", (long)lay);
|
|
for (l = lay; l; l = l->l_next) {
|
|
if (l->l_layfn == &WinLf || l->l_layfn == &BlankLf)
|
|
break;
|
|
debug1("- killing %#lx\n", (long)l);
|
|
if (oldflayer == l)
|
|
oldflayer = 0;
|
|
for (cv = l->l_cvlist; cv; cv = ncv) {
|
|
ncv = cv->c_lnext;
|
|
cv->c_layer = 0;
|
|
cv->c_lnext = 0;
|
|
}
|
|
}
|
|
flayer = lay;
|
|
while (flayer != l)
|
|
ExitOverlayPage();
|
|
flayer = oldflayer;
|
|
}
|
|
|
|
|
|
|
|
int InitOverlayPage(int datasize, struct LayFuncs *lf, int block)
|
|
{
|
|
char *data;
|
|
struct layer *newlay;
|
|
struct canvas *cv, *cvp, **cvpp;
|
|
struct win *p;
|
|
|
|
ASSERT(flayer);
|
|
|
|
cv = 0;
|
|
if (display && D_forecv->c_layer == flayer)
|
|
cv = D_forecv; /* work only on this cv! */
|
|
|
|
if ((newlay = (struct layer *)calloc(1, sizeof(struct layer))) == 0) {
|
|
Msg(0, "No memory for layer struct");
|
|
return -1;
|
|
}
|
|
debug2("Entering new layer on top of %#lx: %#lx\n", (long)flayer, (long)newlay);
|
|
data = 0;
|
|
if (datasize) {
|
|
if ((data = calloc(1, datasize)) == 0) {
|
|
free((char *)newlay);
|
|
Msg(0, "No memory for layer data");
|
|
return -1;
|
|
}
|
|
}
|
|
p = Layer2Window(flayer);
|
|
|
|
if (p && (p->w_savelayer == flayer || (block && flayer->l_next == 0))) {
|
|
if (p->w_savelayer && p->w_savelayer != flayer && p->w_savelayer->l_cvlist == 0)
|
|
KillLayerChain(p->w_savelayer);
|
|
p->w_savelayer = newlay;
|
|
}
|
|
|
|
if (cv && flayer->l_next == 0 && !block) {
|
|
struct display *olddisplay = display;
|
|
display = cv->c_display;
|
|
RemoveStatus();
|
|
display = olddisplay;
|
|
|
|
/* new branch -> just get canvas vps */
|
|
for (cvpp = &flayer->l_cvlist; (cvp = *cvpp); cvpp = &cvp->c_lnext)
|
|
if (cvp == cv)
|
|
break;
|
|
|
|
ASSERT(cvp);
|
|
*cvpp = cv->c_lnext;
|
|
newlay->l_cvlist = cv;
|
|
cv->c_lnext = 0;
|
|
cv->c_layer = newlay;
|
|
}
|
|
else {
|
|
LAY_DISPLAYS(flayer, RemoveStatus());
|
|
if (block)
|
|
debug("layer is blocking\n");
|
|
if (block && flayer->l_layfn == &WinLf) {
|
|
debug("...and is first, so window gets blocked\n");
|
|
ASSERT(p->w_blocked == 0);
|
|
p->w_blocked++;
|
|
newlay->l_blocking = 1;
|
|
}
|
|
|
|
/* change all canvases */
|
|
newlay->l_cvlist = flayer->l_cvlist;
|
|
|
|
for (cvp = newlay->l_cvlist; cvp; cvp = cvp->c_lnext)
|
|
cvp->c_layer = newlay;
|
|
flayer->l_cvlist = 0;
|
|
}
|
|
newlay->l_width = flayer->l_width;
|
|
newlay->l_height = flayer->l_height;
|
|
newlay->l_encoding = 0;
|
|
newlay->l_layfn = lf;
|
|
newlay->l_data = data;
|
|
newlay->l_next = flayer;
|
|
newlay->l_bottom = flayer->l_bottom;
|
|
flayer = newlay;
|
|
LayRestore();
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern struct layout *layouts;
|
|
|
|
void ExitOverlayPage()
|
|
{
|
|
struct layer *oldlay;
|
|
struct win *p;
|
|
int doredisplay = 0;
|
|
struct canvas *cv, *ocv;
|
|
struct layout *lay;
|
|
|
|
ASSERT(flayer);
|
|
debug1("Exiting layer %#lx\n", (long)flayer);
|
|
oldlay = flayer;
|
|
if (oldlay->l_data) {
|
|
if (oldlay->l_layfn->lf_LayFree)
|
|
LayFree(oldlay->l_data);
|
|
free(oldlay->l_data);
|
|
}
|
|
p = Layer2Window(flayer);
|
|
|
|
flayer = oldlay->l_next;
|
|
if (flayer->l_layfn == &WinLf) {
|
|
if (oldlay->l_blocking) {
|
|
ASSERT(p->w_blocked > 0);
|
|
p->w_blocked--;
|
|
debug1("layer was blocking, -> w_blocked now %d\n", p->w_blocked);
|
|
}
|
|
|
|
/* don't warp dead layers: check cvlist */
|
|
if (p->w_blocked && p->w_savelayer && p->w_savelayer != flayer && oldlay->l_cvlist) {
|
|
debug("warping to top of blocking chain!\n");
|
|
/* warp ourself into savelayer */
|
|
flayer = p->w_savelayer;
|
|
doredisplay = 1;
|
|
}
|
|
}
|
|
|
|
if (p && p->w_savelayer == oldlay)
|
|
p->w_savelayer = flayer;
|
|
#ifdef COPY_PASTE
|
|
if (p && oldlay == p->w_paster.pa_pastelayer)
|
|
p->w_paster.pa_pastelayer = 0;
|
|
#endif
|
|
|
|
for (lay = layouts; lay; lay = lay->lay_next)
|
|
for (cv = lay->lay_cvlist; cv; cv = cv->c_next)
|
|
if (cv->c_layer == oldlay)
|
|
cv->c_layer = flayer;
|
|
|
|
/* add all canvases back into next layer's canvas list */
|
|
for (ocv = 0, cv = oldlay->l_cvlist; cv; cv = cv->c_lnext) {
|
|
cv->c_layer = flayer;
|
|
ocv = cv;
|
|
}
|
|
if (ocv) {
|
|
cv = flayer->l_cvlist;
|
|
ocv->c_lnext = 0;
|
|
flayer->l_cvlist = oldlay->l_cvlist;
|
|
|
|
/* redisplay only the warped cvs */
|
|
if (doredisplay)
|
|
LRefreshAll(flayer, 0);
|
|
ocv->c_lnext = cv;
|
|
}
|
|
oldlay->l_cvlist = 0;
|
|
LayerCleanupMemory(oldlay);
|
|
free((char *)oldlay);
|
|
LayRestore();
|
|
LaySetCursor();
|
|
}
|
|
|
|
int LayProcessMouse(struct layer *l, unsigned char ch)
|
|
{
|
|
/* XXX: Make sure the layer accepts mouse events */
|
|
int len;
|
|
if (l->l_mouseevent.len >= sizeof(l->l_mouseevent.buffer))
|
|
return -1;
|
|
|
|
len = l->l_mouseevent.len++;
|
|
l->l_mouseevent.buffer[len] = (len > 0 ? ch - 33 : ch);
|
|
return (l->l_mouseevent.len == sizeof(l->l_mouseevent.buffer));
|
|
}
|
|
|
|
void LayProcessMouseSwitch(struct layer *l, int s)
|
|
{
|
|
if ((l->l_mouseevent.start = s))
|
|
l->l_mouseevent.len = 0;
|
|
}
|
|
|
|
|
|
void LayPause(struct layer *layer, int pause)
|
|
{
|
|
struct canvas *cv;
|
|
int line;
|
|
struct win *win;
|
|
pause = !!pause;
|
|
|
|
if (layer->l_pause.d == pause)
|
|
return;
|
|
|
|
if ((layer->l_pause.d = pause)) {
|
|
/* Start pausing */
|
|
layer->l_pause.top = layer->l_pause.bottom = -1;
|
|
return;
|
|
}
|
|
|
|
/* Unpause. So refresh the regions in the displays! */
|
|
if (layer->l_pause.top == -1 && layer->l_pause.bottom == -1)
|
|
return;
|
|
|
|
if (layer->l_layfn == &WinLf) /* Currently, this will always be the case! */
|
|
win = layer->l_data;
|
|
else
|
|
win = NULL;
|
|
|
|
for (cv = layer->l_cvlist; cv; cv = cv->c_lnext) {
|
|
struct viewport *vp;
|
|
|
|
if (!cv->c_slorient)
|
|
continue; /* Wasn't split, so already updated. */
|
|
display = cv->c_display;
|
|
for (vp = cv->c_vplist; vp; vp = vp->v_next) {
|
|
for (line = layer->l_pause.top; line <= layer->l_pause.bottom; line++) {
|
|
int xs, xe;
|
|
if (line + vp->v_yoff >= vp->v_ys && line + vp->v_yoff <= vp->v_ye &&
|
|
((xs = layer->l_pause.left[line]) >= 0) &&
|
|
((xe = layer->l_pause.right[line]) >= 0)) {
|
|
xs += vp->v_xoff;
|
|
xe += vp->v_xoff;
|
|
|
|
if (xs < vp->v_xs) xs = vp->v_xs;
|
|
if (xe > vp->v_xe) xe = vp->v_xe;
|
|
|
|
#if defined(DW_CHARS) && defined(UTF8)
|
|
if (layer->l_encoding == UTF8 && xe < vp->v_xe && win) {
|
|
struct mline *ml = win->w_mlines + line;
|
|
if (dw_left(ml, xe, UTF8))
|
|
xe++;
|
|
}
|
|
#endif
|
|
|
|
if (xs <= xe)
|
|
RefreshLine(line + vp->v_yoff, xs, xe, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cv == D_forecv) {
|
|
int cx = layer->l_x + cv->c_xoff;
|
|
int cy = layer->l_y + cv->c_yoff;
|
|
|
|
if (cx < cv->c_xs) cx = cv->c_xs;
|
|
if (cy < cv->c_ys) cy = cv->c_ys;
|
|
if (cx > cv->c_xe) cx = cv->c_xe;
|
|
if (cy > cv->c_ye) cy = cv->c_ye;
|
|
|
|
GotoPos(cx, cy);
|
|
}
|
|
}
|
|
|
|
for (line = layer->l_pause.top; line <= layer->l_pause.bottom; line++)
|
|
layer->l_pause.left[line] = layer->l_pause.right[line] = -1;
|
|
}
|
|
|
|
void LayPauseUpdateRegion(struct layer *layer, int xs, int xe, int ys, int ye)
|
|
{
|
|
if (!layer->l_pause.d)
|
|
return;
|
|
if (ys < 0)
|
|
ys = 0;
|
|
if (ye >= layer->l_height)
|
|
ye = layer->l_height - 1;
|
|
if (xe >= layer->l_width)
|
|
xe = layer->l_width - 1;
|
|
|
|
if (layer->l_pause.top == -1 || layer->l_pause.top > ys)
|
|
layer->l_pause.top = ys;
|
|
if (layer->l_pause.bottom < ye) {
|
|
layer->l_pause.bottom = ye;
|
|
|
|
if (layer->l_pause.lines <= ye) {
|
|
int o = layer->l_pause.lines;
|
|
layer->l_pause.lines = ye + 32;
|
|
layer->l_pause.left = realloc(layer->l_pause.left, sizeof(int) * layer->l_pause.lines);
|
|
layer->l_pause.right = realloc(layer->l_pause.right, sizeof(int) * layer->l_pause.lines);
|
|
while (o < layer->l_pause.lines) {
|
|
layer->l_pause.left[o] = layer->l_pause.right[o] = -1;
|
|
o++;
|
|
}
|
|
}
|
|
}
|
|
while (ys <= ye) {
|
|
if (layer->l_pause.left[ys] == -1 || layer->l_pause.left[ys] > xs)
|
|
layer->l_pause.left[ys] = xs;
|
|
if (layer->l_pause.right[ys] < xe)
|
|
layer->l_pause.right[ys] = xe;
|
|
ys++;
|
|
}
|
|
}
|
|
|
|
void LayerCleanupMemory(struct layer *layer)
|
|
{
|
|
if (layer->l_pause.left)
|
|
free(layer->l_pause.left);
|
|
if (layer->l_pause.right)
|
|
free(layer->l_pause.right);
|
|
}
|