ref: d872057643707f83e541f736178427fc17033eb7
dir: /castor.c/
#include <u.h> #include <libc.h> #include <libsec.h> #include <String.h> #include <regexp.h> #include <draw.h> #include <event.h> #include <keyboard.h> #include <panel.h> #include <bio.h> #include <stdio.h> #include <ctype.h> //#include <plumb.h> #include "dat.h" #include "icons.h" void texthit(Panel *p, int b, Rtext *t); void message(char *s, ...); Image *backi; Image *fwdi; Image *reloadi; Panel *root; Panel *backp; Panel *fwdp; Panel *reloadp; Panel *entryp; Panel *urlp; Panel *textp; Panel *statusp; Panel *popup; Url *current_url; Mouse *mouse; Hist *hist = nil; enum { Mback, Mforward, Msearch, Mexit, }; char *menu3[] = { "back", "forward", "search", "exit", 0 }; char * cleanup(char *line) { char *src, *dst; for (src = dst = line; *src != '\0'; src++) { *dst = *src; if (*dst != '\r' && *dst != '\n') dst++; } *dst = '\0'; return line; } void set_current_url(Url *url) { free(current_url); current_url = url; } Url * parseurl(char *url) { char *server, *port, *s, *e; Url *u; url = strdup(url); if((s = strpbrk(url, ":/")) != nil && s[0] == ':' && s[1] == '/' && s[2] == '/'){ server = s + 3; } else { s = smprint("gemini://%s", url); free(url); url = s; server = s + 9; } port = strdup("1965"); if((e = strpbrk(server, ":/")) != nil){ s = mallocz(e-server+1, 1); memmove(s, server, e-server); server = s; if(*e == ':'){ port = strdup(e+1); if((e = strchr(port, '/')) != nil) *e = 0; } } else { server = strdup(server); } u = calloc(1, sizeof(*u)); u->url = url; u->server = server; u->port = port; return u; } void gemini_get(Url *url) { Thumbprint *th; TLSconn conn; int fd; char *line; Biobuf body; Gmenu *m; m = malloc(sizeof *m); if(m==nil) sysfatal("malloc: %r"); m->text = nil; plrtstr(&m->text, 1000000, 0, 0, font, strdup(" "), 0, 0); set_current_url(url); message("loading %s...", url->url); char *naddr = netmkaddr(url->server, "tcp", url->port); fd = dial(naddr, 0, 0, 0); if(fd < 0){ message("unable to connect to %s: %r", url->server); return; } conn.serverName = url->server; memset(&conn, 0, sizeof(conn)); fd = tlsClient(fd, &conn); th = initThumbprints("/sys/lib/ssl/gemini", nil, "x509"); if(th != nil){ okCertificate(conn.cert, conn.certlen, th); freeThumbprints(th); free(conn.cert); } fprint(fd, "%s\r\n", url->url); Binit(&body, fd, OREAD); while((line = Brdstr(&body, '\n', 0)) != nil){ if (strstr(line, "=>") == nil) { //String *lines[]; //int line_count = 0; //String *cut_line; //cut_line = s_new(); //char delim[] = " "; //char *ptr = strtok(line, delim); //while(ptr != NULL) //{ // if(s_len(cut_line)+strlen(ptr)<80){ // s_append(cut_line, strcat(ptr, " ")); // }else{ // lines[line_count] = cut_line; // line_count++; // cut_line = s_new(); // s_append(cut_line, ptr); // } // ptr = strtok(NULL, delim); //} //for (int i = 0; i < line_count; i++){ // plrtstr(&m->text, 1000000, 8, 0, font, strdup(s_to_c(lines[i])), 0, 0); //} plrtstr(&m->text, 1000000, 8, 0, font, strdup(cleanup(line)), 0, 0); } else { plrtstr(&m->text, 1000000, 8, 0, font, strdup(cleanup(line)), PL_HOT, 0); } free(line); } Bflush(&body); close(fd); plinittextview(textp, PACKE|EXPAND, ZP, m->text, texthit); pldraw(textp, screen); plinitlabel(urlp, PACKN|FILLX, strdup(url->url)); pldraw(urlp, screen); message("Castor!"); message("%d", textp->r.max.x); } void menuhit(int button, int item) { USED(button); switch(item){ case Mback: //backhit(backp, 1); break; case Mforward: //nexthit(fwdp, 1); break; case Msearch: //search(); break; case Mexit: exits(nil); break; } } void entryhit(Panel *p, char *t) { USED(p); switch(strlen(t)){ case 0: return; case 1: switch(*t){ case 'b': //backhit(backp, 1); break; case 'n': //nexthit(fwdp, 1); break; case 'q': exits(nil); break; default: message("unknown command %s", t); break; } break; default: gemini_get(parseurl(t)); } plinitentry(entryp, PACKN|FILLX, 0, "", entryhit); pldraw(root, screen); } void texthit(Panel *p, int b, Rtext *rt) { message("text hit %s", rt->text); char *t, *u, *next_url; char *s = rt->text; next_url = nil; if (s[0] == '=' && s[1] == '>') { t = s + 2; while (isspace(*t)) { t++; u = t; if ((t = strpbrk(t, " :/")) == nil || t[0] != ':' || t[1] != '/' || t[2] != '/') { /* no scheme */ if (*u == '/') { /* absolute */ char *path = strtok(u, " "); next_url = smprint("gemini://%s:%s/%s", current_url->server, current_url->port, path+1); } else { char *path = strtok(u, " "); next_url = smprint("gemini://%s:%s/%s", current_url->server, current_url->port, path); } } else { next_url = strtok(u, " "); } } //free(s); gemini_get(parseurl(next_url)); //message(next_url); } } void message(char *s, ...) { static char buf[1024]; char *out; va_list args; va_start(args, s); out = buf + vsnprint(buf, sizeof(buf), s, args); va_end(args); *out='\0'; plinitlabel(statusp, PACKN|FILLX, buf); pldraw(statusp, screen); flushimage(display, 1); } void mkpanels(void) { Panel *p, *ybar, *xbar, *m; m = plmenu(0, 0, menu3, PACKN|FILLX, menuhit); root = plpopup(0, EXPAND, 0, 0, m); p = plgroup(root, PACKN|FILLX); statusp = pllabel(p, PACKN|FILLX, "Castor!"); plplacelabel(statusp, PLACEW); //plbutton(p, PACKW|BITMAP|NOBORDER, backi, backhit); //plbutton(p, PACKW|BITMAP|NOBORDER, fwdi, nexthit); //plbutton(p, PACKW|BITMAP|NOBORDER, reloadi, reloadhit); pllabel(p, PACKW, "Go: "); entryp = plentry(p, PACKN|FILLX, 0, "", entryhit); p = plgroup(root, PACKN|FILLX); urlp = pllabel(p, PACKN|FILLX, ""); plplacelabel(urlp, PLACEW); p = plgroup(root, PACKN|EXPAND); ybar = plscrollbar(p, PACKW|USERFL); xbar = plscrollbar(p, IGNORE); textp = pltextview(p, PACKE|EXPAND, ZP, nil, nil); plscroll(textp, xbar, ybar); plgrabkb(entryp); } void eresized(int new) { if(new && getwindow(display, Refnone)<0) sysfatal("cannot reattach: %r"); plpack(root, screen->r); pldraw(root, screen); } Image* loadicon(Rectangle r, uchar *data, int ndata) { Image *i; int n; i = allocimage(display, r, RGBA32, 0, DNofill); if(i==nil) sysfatal("allocimage: %r"); n = loadimage(i, r, data, ndata); if(n<0) sysfatal("loadimage: %r"); return i; } void loadicons(void) { Rectangle r = Rect(0,0,16,16); backi = loadicon(r, ibackdata, sizeof ibackdata); fwdi = loadicon(r, ifwddata, sizeof ifwddata); reloadi = loadicon(r, ireloaddata, sizeof ireloaddata); } void scrolltext(int dy, int whence) { Scroll s; s = plgetscroll(textp); switch(whence){ case 0: s.pos.y = dy; break; case 1: s.pos.y += dy; break; case 2: s.pos.y = s.size.y+dy; break; } if(s.pos.y > s.size.y) s.pos.y = s.size.y; if(s.pos.y < 0) s.pos.y = 0; plsetscroll(textp, s); /* BUG: there is a redraw issue when scrolling This fixes the issue albeit not properly */ pldraw(textp, screen); } void main(int argc, char *argv[]) { Event e; Response *r; Url *url; if(argc == 2) url = parseurl(argv[1]); else url = parseurl("gemini.circumlunar.space/capcom/"); quotefmtinstall(); if(initdraw(nil, nil, "gemini")<0) sysfatal("initdraw: %r"); einit(Emouse|Ekeyboard); plinit(screen->depth); loadicons(); mkpanels(); gemini_get(url); eresized(0); for(;;){ switch(event(&e)){ case Ekeyboard: switch(e.kbdc){ default: plgrabkb(entryp); plkeyboard(e.kbdc); break; case Khome: scrolltext(0, 0); break; case Kup: scrolltext(-textp->size.y/4, 1); break; case Kpgup: scrolltext(-textp->size.y/2, 1); break; case Kdown: scrolltext(textp->size.y/4, 1); break; case Kpgdown: scrolltext(textp->size.y/2, 1); break; case Kend: scrolltext(-textp->size.y, 2); break; case Kdel: exits(nil); break; } break; case Emouse: mouse = &e.mouse; if(mouse->buttons & (8|16) && ptinrect(mouse->xy, textp->r)){ if(mouse->buttons & 8) scrolltext(textp->r.min.y - mouse->xy.y, 1); else scrolltext(mouse->xy.y - textp->r.min.y, 1); break; } plmouse(root, mouse); /* BUG: there is a redraw issue when scrolling This fixes the issue albeit not properly */ //pldraw(textp, screen); break; } } }