#include #include #include #include #include #include #include "guifs.h" Point mousexy; Mousectl *mousectl; Keyboardctl *keyboardctl; Channel *updatechan; Channel *mkcolourchan; Channel *newcolourchan; void drawgui(GuiElement *g) { rlock(&g->lock); GuiSpec spec = guispecs[g->type]; if(memcmp(&g->rect, &g->border, sizeof(Rectangle)) != 0 && Dx(g->border) > 0 && Dy(g->border) > 0){ /* draw the border first */ Image *bc = getprop(g, Pbordercolour, 1).colour->image; Rectangle r; /* top part */ r.min.x = g->border.min.x; r.min.y = g->border.min.y; r.max.x = g->border.max.x; r.max.y = g->rect.min.y; draw(screen, r, bc, nil, ZP); /* right part */ r.min.x = g->rect.max.x; r.min.y = g->rect.min.y; r.max.x = g->border.max.x; r.max.y = g->border.max.y; draw(screen, r, bc, nil, ZP); /* bottom part */ r.min.x = g->border.min.x; r.min.y = g->rect.max.y; r.max.x = g->border.max.x; r.max.y = g->border.max.y; draw(screen, r, bc, nil, ZP); /* left part */ r.min.x = g->border.min.x; r.min.y = g->border.min.y; r.max.x = g->rect.min.x; r.max.y = g->border.max.y; draw(screen, r, bc, nil, ZP); } if(Dx(g->rect) > 0 && Dy(g->rect) > 0){ /* Draw the background */ Image *bg = getprop(g, Pbackground, 1).colour->image; draw(screen, g->rect, bg, nil, ZP); spec.draw(g); for(int i = 0; i < g->nchildren; i++) drawgui(g->children[i]); } runlock(&g->lock); } void drawcontainer(GuiElement *g) { USED(g); } void drawtext(Rectangle rect, Rune *text, Image *fg, int firstline, int *nlinesp, int *endlinep) { Point p = rect.min; int w; int nlines = 1; int newlines = 0; int endline = 0; int ended = 0; int xoffset = 0; for(Rune *r = text; *r; r++){ switch(*r){ case '\n': newlines++; Lnewline: if(ended && endline == 0) endline = nlines; p.x = rect.min.x; xoffset = 0; if((newlines)-(*r=='\n') >= firstline) p.y += font->height; nlines++; break; case '\t': w = 8-(xoffset%8); xoffset += w; w *= stringnwidth(font, " ", 1); xoffset += w; if(p.x+w > rect.max.x){ r--; goto Lnewline; }else p.x += w; break; default: xoffset++; w = runestringnwidth(font, r, 1); if(p.x+w > rect.max.x){ r--; goto Lnewline; } if(!ended && (p.y+font->height) < rect.max.y){ if(newlines >= firstline) runestringn(screen, p, fg, ZP, font, r, 1); }else ended = 1; p.x += w; break; } } if(endline == 0) endline = nlines; if(nlinesp) *nlinesp = nlines; if(endlinep) *endlinep = endline; } void drawtextbox(GuiElement *g) { Rune *text = getprop(g, Ptext, 0).text; Image *fg = getprop(g, Ptextcolour, 1).colour->image; Image *scrollbg = getprop(g, Pbordercolour, 1).colour->image; Image *scrollfg = getprop(g, Pbackground, 1).colour->image; int firstline = getprop(g, Pscroll, 1).scroll; int lines = 0; for(Rune *r = text; *r; r++) lines += (*r) == '\n'; /* draw the scroll bar background here, then draw the actual bar later */ Rectangle scrollbar = Rect(g->rect.min.x, g->rect.min.y, g->rect.min.x+ScrollbarWidth, g->rect.max.y); Rectangle textrect = g->content; textrect.min.x += ScrollbarWidth; draw(screen, scrollbar, scrollbg, nil, ZP); int nlines, endline; drawtext(textrect, text, fg, firstline, &nlines, &endline); int height = Dy(scrollbar); scrollbar.max.x -= 1; scrollbar.min.y += (height * (firstline)) / nlines; scrollbar.max.y -= (height * (nlines-endline)) / nlines; if(scrollbar.min.y > (g->rect.max.y - 2)) scrollbar.min.y = g->rect.max.y - 2; draw(screen, scrollbar, scrollfg, nil, ZP); if(firstline > nlines){ PropVal p; p.scroll = nlines - 1; setprop(g, Pscroll, p, 0); /* TODO: we don't have the write lock here :) */ } } Colour * mkcolour(ulong c) { Colour *res = nil; send(mkcolourchan, &c); recv(newcolourchan, &res); return res; } void updategui(int new) { /* Trigger a call to resized by sending a message */ send(updatechan, &new); } void resized(int new) { if(new && getwindow(display, Refnone) < 0) sysfatal("can't reattach to window: %r"); if(root != nil){ layout(root, screen->r); drawgui(root); flushimage(display, 1); } } void guiproc(void *) { int i; ulong c; Rune r; if(initdraw(nil, nil, "guifs") < 0) sysfatal("initdraw failed"); if((mousectl = initmouse(nil, screen)) == nil) sysfatal("initmouse failed"); if((keyboardctl = initkeyboard(nil)) == nil) sysfatal("initkeyboard failed"); enum { Aupdategui, Aresize, Amkcolour, Amouse, Akeyboard, Aaltend, }; Alt alts[] = { [Aupdategui] = {updatechan, &i, CHANRCV}, [Aresize] = {mousectl->resizec, nil, CHANRCV}, [Amkcolour] = {mkcolourchan, &c, CHANRCV}, [Amouse] = {mousectl->c, &mousectl->Mouse, CHANRCV}, [Akeyboard] = {keyboardctl->c, &r, CHANRCV}, [Aaltend] = {nil, nil, CHANEND}, }; while(1){ int which = alt(alts); switch(which){ case Aupdategui: resized(i); break; case Aresize: resized(1); break; case Amkcolour: { Colour *col = emalloc(sizeof(Colour)); col->image = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c); col->code = c; send(newcolourchan, &col); } break; case Amouse: mousexy = mousectl->Mouse.xy; if(!root) break; if(mouseevent(mousectl->Mouse)) resized(0); break; case Akeyboard: if(!root) break; if(keyboardevent(r)) resized(0); break; } } } void initgraphics(void) { updatechan = chancreate(sizeof(int), 0); mkcolourchan = chancreate(sizeof(ulong), 0); newcolourchan = chancreate(sizeof(Colour *), 0); proccreate(guiproc, nil, mainstacksize); updategui(1); }