#include #include #include #include #include #include #include #include "pane.h" #include "x11.h" #include "util.h" #include "defaults.h" #include "resizeable_buf.h" #include "graphic-chars.h" #define LINE_BLANK { RB_BLANK, false, false } #define CELL(s,x,y) ((s)->cells[(y)*(s)->width + (x)]) #define TOUCHED(s,x,y) ((s)->touched[(y)*(s)->width + (x)]) static void pane_sane(struct pane *p); static bool constrain_coords(int width, int height, int *restrict sx, int *restrict sy, int *restrict w, int *restrict h); static void line_fini(struct line *l) { RB_FREE(&l->cs); } struct pane * pane_create(sc_t *sc) { struct pane *s = malloc(sizeof(struct pane)); s->width = 0; s->height = 0; s->cursor_x = 0; s->cursor_y = 0; s->show_cursor = true; s->gc = win.gc; //s->cursor_color = DC_cursorColor; s->cursor_color = NULL; //get_charcell_size(&s->cwidth, &s->cheight); s->cwidth = s->cheight = -1; s->rr.x1 = s->rr.x2 = -1; s->rr.y1 = s->rr.y2 = -1; s->scrollback = 0; RB_INIT(&s->lines); s->d = NULL; s->sc = sc; sc->pane = s; return s; } void pane_attach(struct pane *p, struct conn *dpy) { assert(dpy); p->cursor_color = DC_cursorColor; get_charcell_size(&p->cwidth, &p->cheight); } void pane_scrollback(struct pane *p, int s) { int nl = RBL(struct line, &p->lines); int op = p->scrollback; if(!s || nl < p->height) p->scrollback = 0; else { p->scrollback += s; p->scrollback = CLAMP(p->scrollback, 0, nl - p->height); } if(op != p->scrollback) { pane_touchcell(p,0,0); pane_touchcell(p,p->width,p->height); } } void pane_setup(struct pane *p, int w, int h) { p->width = w; p->height = h; p->cursor_x = MIN(p->cursor_x, w - 1); p->cursor_y = MIN(p->cursor_y, h - 1); pane_touchcell(p,0,0); pane_touchcell(p,p->width,p->height); int n = h - RBL(struct line, &p->lines); if(n > 0) pane_scroll(p,n, true); } void pane_settext_generic(struct pane * restrict p, int x, int y, void const * restrict s, int len, struct cell style, bool latin1) { int i; assert(x >= 0 && y >= 0); if (x >= p->width) return; if (x + len > p->width - 1) len -= x + len - (p->width - 1); if(len <= 0) return; assert (x + len < p->width); if (y >= p->height) return; pane_touchcell(p, x, y); pane_touchcell(p, x + len, y); RBP(struct line, &p->lines)[RBL(struct line, &p->lines) - p->height + y].vestigial = false; for(i = 0; i < len; i++) { assert(x + i < p->width); assert(y < p->height); struct cell *c = cell(p, x + i, y, true); *c = style; if(latin1) c->ch = ((char *)s)[i]; else c->ch = ((wchar_t *)s)[i]; } } struct cell * cell(struct pane *p, int x, int y, bool create) { int nl = RBL(struct line, &p->lines); int ny = y + MAX(0, nl - p->height); if(ny < 0 || x < 0) return NULL; if(create) { while (ny >= nl) { struct line l = LINE_BLANK; RB_APPEND(struct line, &p->lines, l); nl++; } struct line *l = &RBP(struct line, &p->lines)[ny]; if(x >= RBL(struct cell, &l->cs)) { rb_resize_zero(&l->cs, (x + 1) * sizeof(struct cell), true); } } if(ny >= nl) return NULL; struct line *l = &RBP(struct line, &p->lines)[ny]; int sx = RBL(struct cell, &l->cs); if(x >= sx) return NULL; return RBP(struct cell, &l->cs) + x; } void pane_clear(struct pane *p, int x, int y, int w, int h, struct cell style) { if(constrain_coords(p->width, p->height, &x, &y, &w, &h)) return; pane_touchcell(p, x, y); pane_touchcell(p, x + w, y + h); if(w == p->width && h == p->height && x == 0 && y == 0) { pane_slideclear(p); return; } if(0 == x && w == p->width) { struct line *l = RBP(struct line, &p->lines) + (RBL(struct line, &p->lines) - p->height); for(int i = 0; i < h; i++) { RB_CLEAR(l[i + y].cs); } return; } int i; assert(w >= 0 && h >= 0 && x >= 0 && y >= 0); if( w == 0 || h == 0) return; while(h && y < p->height ) { for(i=0;iwidth;i++) { struct cell *c = cell(p,x + i,y, true); if(c) *c = style; } h--;y++; } } void pane_touchall(struct pane *p) { //p->redraw_all = 1; pane_touchcell(p,0,0); pane_touchcell(p,p->width,p->height); } void pane_toucharea(struct pane *p, int x, int y, int w, int h) { if(!w || !h) return; pane_touchcell(p, (x)/p->cwidth, (y)/p->cheight); pane_touchcell(p, (x + w)/p->cwidth, (y + h)/p->cheight); } void pane_touchcell(struct pane *p, int x, int y) { if(!IS_ATTACHED()) return; assert(x >= 0 && y >= 0); if(p->rr.x1 == -1 ) { p->rr.x1 = p->rr.x2 = x; p->rr.y1 = p->rr.y2 = y; } else { p->rr.x1 = MIN(p->rr.x1, x); p->rr.y1 = MIN(p->rr.y1, y); p->rr.x2 = MAX(p->rr.x2, x); p->rr.y2 = MAX(p->rr.y2, y); } } void pane_showcursor(struct pane *p, bool show) { if(p->show_cursor == show) return; p->show_cursor = show; pane_touchcell(p,p->cursor_x,p->cursor_y); } void pane_setcursor(struct pane *p, int x, int y) { if(p->show_cursor) { pane_touchcell(p,p->cursor_x,p->cursor_y); pane_touchcell(p,x,y); } p->cursor_x = x; p->cursor_y = y; pane_sane(p); } static void pane_sane(struct pane *p) { assert(p->width >= 0); assert(p->height >= 0); assert(!p->width || p->cursor_x <= p->width); assert(!p->height || p->cursor_y < p->height); assert(RBL(struct line, &p->lines) >= p->height); } void pane_slideclear(struct pane *p) { pane_scroll(p, p->height, true); } void pane_scroll(struct pane *p, int y, bool vestigal) { assert(y >= 0); y = MIN(y, p->height); if(!y) return; pane_touchcell(p,0,0); pane_touchcell(p,p->width,p->height); int len = RB_LEN(&p->lines); rb_resize_zero(&p->lines, len + y*sizeof(struct line), true); struct line *end = RBPE(struct line, &p->lines) - y; if(vestigal) for(int i = 0; i < y; i++) end[i].vestigial = true; int start = RBL(struct line, &p->lines) - p->height - y; int count = y; if (start < 0) { count += start; start = 0; } for(int i = start; i < start + count ; ) { struct line *l = RBP(struct line, &p->lines) + i; if(l->vestigial) { assert(!RB_LEN(&l->cs)); line_fini(l); rb_delete(&p->lines, i*sizeof(struct line), sizeof(struct line)); count--; } else i++; } } void pane_partial_scroll(struct pane *p, int sbegin, int send, int y) { int lc = send - sbegin + 1; int mc = lc - abs(y); int dc = lc - mc; if(!y || lc <= 1 || mc <= 0) return; assert(mc > 0 && dc > 0 && mc + dc == lc); int nla = RBL(struct line, &p->lines) - p->height; struct line *a = RBP(struct line, &p->lines) + nla + sbegin; struct line *b = a + dc; struct line *c = a + mc; struct line *from,*to,*setfree,*blank; if ( y > 0) { from = b; to = a; setfree = a; blank = c; } else { from = a; to = b; setfree = c; blank = a; } for(int i = 0; i < dc; i++) line_fini(setfree + i); memmove(to,from, sizeof(struct line)*mc); memset(blank,0,sizeof(struct line)*dc); pane_touchcell(p,0,sbegin); pane_touchcell(p,p->width,sbegin + lc); } static bool constrain_coords(int width, int height, int *restrict sx, int *restrict sy, int *restrict w, int *restrict h) { if ( !width || !height || *sx >= width || *sy >= height) return true; // we are done! if(*sx < 0) { *w += *sx; *sx = 0; } if(*sy < 0) { *h += *sy; *sy = 0; } *w = MIN(width - *sx, *w); *h = MIN(height - *sy, *h); if(*w <=0 || *h <= 0) return true; return false; } static bool constrain_coord(int width, int * restrict s, int * restrict d, int * restrict w) { if(!width) return true; // Shorten *w if *d - *d + w goes off screen if(*d < 0 ) { int a = -(*d); *w -= a; *d += a; *s += a; } if (*d + *w > width - 1) *w -= *d + *w - (width - 1); assert (*d >= 0 && *d + *w < width); if(!*w) return true; assert (*w > 0); return false; } // copy data within a line void pane_line_copy(struct pane *p, int sx, int dx, int y, int w) { if(y >= p->height || y < 0 || w <= 0) return; struct line * l = RBPE(struct line, &p->lines) - p->height + y; if(constrain_coord(p->width, &sx, &dx, &w)) return; assert(w > 0); assert(dx >= 0); assert(dx + w < p->width); int numx = RBL(struct cell, &l->cs); if(dx + w > numx) { rb_resize_zero(&l->cs, (dx + w) * sizeof(struct cell), true); numx = dx + w; } struct cell *c = RBP(struct cell, &l->cs); if(sx < 0) { int ca = MIN(0 - sx,w); memset(c + dx, 0, ca * sizeof(struct cell)); sx = 0; dx += ca; w -= ca; } assert(w >= 0); int ca = MIN(w,MAX(0,numx - sx)); memmove(c + dx, c + sx, ca * sizeof(struct cell)); w -= ca; dx += ca; assert(w >= 0); memset(c + dx, 0, w * sizeof(struct cell)); pane_sane(p); } void pane_draw(struct pane *p, bool use_pixmap, bool clear_area) { Drawable pm; if(!IS_ATTACHED() || p->rr.x1 == -1) return; p->rr.x1 = MAX(p->rr.x1, 0); p->rr.y1 = MAX(p->rr.y1, 0); p->rr.x2 = MIN(p->rr.x2, p->width); p->rr.y2 = MIN(p->rr.y2, p->height); int sx = use_pixmap ? p->rr.x1*p->cwidth : 0; int sy = use_pixmap ? p->rr.y1*p->cheight : 0; int dw = (p->rr.x2 - p->rr.x1 + 1 )*p->cwidth; int dh = (p->rr.y2 - p->rr.y1 + 1 )*p->cheight; if(use_pixmap) { pm = XCreatePixmap(conn->dpy, win.w, dw + 1, dh + 1, DefaultDepth(conn->dpy, win.current_screen)); XSetForeground(conn->dpy, win.gc, DC_background->pixel); XFillRectangle(conn->dpy, pm, win.gc, 0, 0, dw + 1, dh + 1); } else { pm = win.w; if(clear_area) XClearArea(conn->dpy, win.w, p->rr.x1*p->cwidth, p->rr.y1*p->cheight, dw, dh, False); } if(!p->d) p->d = XftDrawCreate(conn->dpy, pm, DefaultVisual(conn->dpy, win.current_screen), DefaultColormap(conn->dpy, win.current_screen)); else XftDrawChange(p->d, pm); for(int j = p->rr.y1; j <= p->rr.y2; j++) { for(int i = p->rr.x1; i <= p->rr.x2; i++) { struct cell *c = cell(p,i,j - p->scrollback, false); if(!c) continue; if(!c->fgcolor) c->fgcolor = DC_foreground; if(!c->bgcolor) c->bgcolor = DC_background; XftColor *fg = c->flag.inverse ? &c->bgcolor->rc : &c->fgcolor->rc; XftColor *bg = c->flag.inverse ? &c->fgcolor->rc : &c->bgcolor->rc; int x = i*p->cwidth - sx + sx % 2; int y = j*p->cheight - sy + sy % 2; if(bg->pixel != DC_background->rc.pixel) XftDrawRect(p->d, bg, x,y, p->cwidth , p->cheight ); FcChar32 ch = c->ch; if (!ch) continue; if (!draw_character(ch, NULL, pm, win.gc, 0, 0, 1, 1)) { XSetForeground(conn->dpy, win.gc, fg->pixel); draw_character(ch,conn->dpy,pm,win.gc, x, y, p->cwidth, p->cheight); } else { XftFont *font; int type = 0; if(c->flag.bold) type |= FS_BOLD; if(c->flag.italic) type |= FS_ITALIC; font_lookup_char(conn, ch, win.current_screen, type, &font); if(font) XftDrawString32(p->d, fg, font, x, y + win.fn->ascent, &ch, 1); } } } if(p->show_cursor && !p->scrollback) { XSetLineAttributes(conn->dpy, win.gc, 1, LineSolid, CapButt, JoinMiter); XSetForeground(conn->dpy, win.gc, p->cursor_color->pixel); XDrawRectangle(conn->dpy, pm, win.gc, p->cwidth * p->cursor_x - sx + sx % 2, p->cheight * p->cursor_y - sy + sy % 2, p->cwidth - 1, p->cheight - 1 ); } if(use_pixmap) { XCopyArea(conn->dpy, pm, win.w, win.gc, sx % 2, sy % 2, dw, dh, sx,sy); XFreePixmap(conn->dpy, pm); } p->rr.x1 = p->rr.x2 = -1; p->rr.y1 = p->rr.y2 = -1; } void pane_detach(struct pane *p) { graphic_chars_detach(); XftDrawDestroy(p->d); p->d = NULL; }