Four Years with Decker
Posted by ibobev 2 days ago
Comments
Comment by nikitau 1 day ago
Was completely unaware of Decker before reading this, and wanted to say that it is unlike anything I had seen before. But then it reminded me of things like the Arduboy or PICO-8 in being a kind of alt-history tech by spawning an ecosystem around an old mac-style UI, instead of a handheld console UI like the former two.
I'm curious what OPs motivation for using it is. Is it a sort of "limitation bebreeds creativity" thing or something else?
Is there a name for this for of projects more broadly?
Comment by veqq 1 day ago
Comment by ingenieros 1 day ago
Comment by nikitau 1 day ago
But I can definitely relate to the yearning for older, clunkier, "quieter" tech, in this age of ubiquitous HTML, CSS, JS. These maybe seemingly naive assumptions like mixing code and presentation, suboptimal UX, and the fact that people still enjoy developing for them for fun rather than for some SaaS end, feels, I don't know, cozy?
Comment by ingenieros 1 day ago
Do yourself a favor and play with the original via a simulator https://infinitemac.org/1996/System%207.5.3.
Comment by RodgerTheGreat 20 hours ago
Comment by ingenieros 20 hours ago
Comment by xyzzy_plugh 1 day ago
Decker is one of the craziest code bases I'm aware of: a single, incredibly dense file:
https://raw.githubusercontent.com/JohnEarnest/Decker/refs/he...
(I wonder how many tokens it'd be to make this legible...)
Comment by icen 1 day ago
I think there are far worse codebases!
Comment by xyzzy_plugh 23 hours ago
Here, I inserted a subtle bug in this function, can you find it?
int widget_grid(lv*target,grid x,grid_val*value){
if(x.show==show_none)return 0; lv*hfnt=FONT_BODY; int hsize=x.headers?font_h(hfnt)+5:0; if(x.size.h<=(50+hsize)||x.size.w<16)x.scrollbar=0;
lv*fnt=x.font?x.font:FONT_MONO; int os=value->scroll, or=value->row, oc=value->col, files=x.headers==2; if(files||x.size.h<=hsize)x.headers=0;
int _bg=dgeti(value->table,lmistr("_bg")),_fg=dgeti(value->table,lmistr("_fg")),_hideby=dgeti(value->table,lmistr("_hideby"));
lv*pv=NULL,*vp=NULL;if(target!=NULL&&_hideby!=-1)grid_pv(target,&pv,&vp);int nr=pv?pv->c: value->table->n;
#define grid_cell_at(col,row) (value->table->lv[col]->lv[permuted_row(row)])
int nc=MIN(256,value->table->c), rh=font_h(fnt)+(x.lines?5:3), fcol=!in_layer()?13:x.show==show_invert?32:1, bcol=x.show==show_invert?1:32;
int fk=strlen(x.format); rect b=x.size;
int sel=in_layer()&&x.show!=show_none&&wid.active==wid.count;
if(in_layer()&&dover(b)&&(ev.md||ev.mu||ev.drag)){if(!sel&&wid.gv)grid_exit(); wid.active=wid.count,sel=1;}
if(sel&&in_layer()&&!over(b)&&ev.md){sel=0;wid.active=-1;grid_exit();}
if(sel){if(wid.fv)field_exit();wid.ingrid=1,wid.g=x,wid.gv=value,wid.gt=target;}
if(x.show!=show_transparent)draw_rect(b,bcol);
rect bh={b.x,b.y,b.w,x.headers?font_h(hfnt)+5:0}; int nrd=MIN(nr,((b.h-bh.h+1)/rh)), scrollmax=nr-nrd;
rect bb=scrollbar((rect){b.x,b.y+(x.headers?bh.h-1:0),b.w,b.h-(x.headers?bh.h-1:0)},scrollmax,1,nrd,&value->scroll,x.scrollbar,x.show==show_invert);
draw_box(x.lines?b:(rect){b.x,bb.y,b.w,b.h-(bb.y-b.y)},0,sel?13:fcol);
char*pal=patterns_pal(ifield(deck,"patterns"));
if(x.lines)draw_rect(bh,fcol);if(nc<=0)draw_textc(inset(bb,1),"(no data)",hfnt,fcol);
int cw[256];for(int z=0;z<nc;z++)cw[z]=z>=x.widths[0]?-1:x.widths[1+z];
if(_bg!=-1)cw[_bg]=0;if(_fg!=-1)cw[_fg]=0;if(_hideby!=-1)cw[_hideby]=0;
int hwt=0;for(int z=0;z<x.widths[0];z++)hwt+=cw[z];
int nwt=0;for(int z=0;z<nc ;z++)nwt+=cw[z]==-1;
for(int z=x.widths[0];z<nc ;z++)cw[z]=cw[z]==-1?(bb.w-hwt)/nwt:0;
if(!cf.sv)cf=str_new();
#define rowh(n) inset((rect){bb.x+1,bb.y+rh*n+2,bb.w-2,rh-3},x.lines?0:-1)
#define rowb(n) (rect){bb.x,bb.y+rh*n,bb.w,rh}
int clicked=0,rsel=0,hrow=-1,hcol=-1;
for(int y=0;y<nrd;y++){
if(_bg!=-1){
int p=CLAMP(0,ln(grid_cell_at(_bg,y+value->scroll)),255);
if(p){rect t=rowb(y);if(y==0)t.y+=1,t.h-=1; draw_rect(t,p);}
}
int ra=in_layer()&&over(bb)&&over(rowb(y));rect cbox={0};
if(ra&&x.bycell){for(int z=0;z<nc;z++){rect cell=grid_cell(x,value,bb,(pair){z,y},cw);if(over(cell))hcol=z,cbox=grid_hcell(x,z,cell);}}
if(ra&&(ev.md||ev.drag)){rsel=1,hrow=y+value->scroll;draw_rect(x.bycell?cbox:rowh(y),fcol);}
if(ra&&ev.mu)clicked=1,value->row=permuted_row(y+value->scroll),value->col=hcol;
if(ra&&!ev.drag)uicursor=cursor_point;
}int rr=display_row(value->row)-value->scroll;
if(!rsel&&rr>=0&&rr<nrd&&(x.bycell?value->col>=0&&value->col<nc:1)){
hrow=display_row(value->row),hcol=value->col;
if(x.bycell&&value->col>=0){draw_rect(grid_hcell(x,value->col,grid_cell(x,value,bb,(pair){value->col,rr},cw)),fcol);}
else{draw_rect(rowh(rr),fcol);}
}
int drawncol=0;for(int z=0,cols=0,cx=0;z<nc&&cx+cw[cols]<=bb.w;z++,cols++){
rect hs=(rect){bh.x+4+cx,bh.y+1,cw[cols]-5,bh.h-2};
if(hs.w<=0)continue; // suppressed column
if(x.headers){
int oa=target&&in_layer()&&over(hs)&&((ev.drag||ev.mu)?dover(hs):1)&&!wid.col_drag;
int dp=oa&&(ev.md||ev.drag);if(dp)draw_rect(hs,x.lines?bcol:fcol);
draw_textc(hs,value->table->kv[z]->sv,hfnt,x.lines^dp?bcol:fcol);
if(oa&&!ev.drag)uicursor=cursor_point;
if(oa&&ev.mu){msg.target_order=target,msg.arg_order=value->table->kv[z];}
}
if(drawncol&&x.lines)draw_invert(pal,(rect){hs.x-3,b.y+1,1,b.h-2});cx+=cw[cols];drawncol=1;
for(int y=0;y<nrd;y++){
int ccol=y+value->scroll==hrow&&(x.bycell?cols==hcol :1)?bcol: _fg==-1?fcol: CLAMP(0,ln(grid_cell_at(_fg,y+value->scroll)),255);
rect cell={hs.x-3,bb.y+rh*y+1,hs.w+5,rh-1}; lv*v=grid_cell_at(z,y+value->scroll);
cf.c=0;format_type_simple(&cf,v,z>fk?'s':x.format[z]=='L'?'s':x.format[z]);str_term(&cf);
rect ib=box_center(cell,image_size(ICONS[0]));pair ip={ib.x,ib.y};
rect oc=frame.clip; frame.clip=box_intersect(cell,frame.clip);
if (z<fk&&x.format[z]=='I'){int i=MAX(0,MIN(8,ln(v)));if(i<8)draw_icon(ip,ICONS[i],ccol);}
else if(z<fk&&x.format[z]=='B'){if(lb(v))draw_icon(ip,ICONS[icon_chek],ccol);}
else if(z<fk&&(x.format[z]=='t'||x.format[z]=='T')){
rect rr={hs.x+1,bb.y+rh*y,hs.w-2,rh}; // display rich text:
pair t=layout_richtext(deck,rtext_cast(v),fnt,align_left,rr.w);
rect r={rr.x,rr.y+ceil(t.y<rr.h?(rr.h-t.y)/2.0:0),rr.w,rr.h};
lv*alink=NULL;if(x.format[z]=='T'&&in_layer()&&(ev.md||ev.drag||ev.mu))for(int z=0;z<layout_count;z++){
glyph_box g=layout[z];if(g.pos.w<1)continue; // skip squashed spaces/newlines
if(g.pos.y+g.pos.h<0||g.pos.y>r.h)continue; g.pos.x+=r.x, g.pos.y+=r.y; // coarse clip
if(lis(g.arg)&&g.arg->c&&dover(g.pos)&&over(g.pos)){
if(ev.md||ev.drag){alink=g.arg;break;}
if(ev.mu){msg.target_link=target,msg.arg_link=g.arg;break;}
}
}
draw_text_rich_raw(r,ccol,0,alink);
}
else{
rect r={hs.x+1,bb.y+rh*y,hs.w-2,rh}; // right-align numeric columns:
if(z<fk&&strchr("fcCihH",x.format[z])){draw_textr(r,cf.sv,fnt,ccol);}else{draw_text_fit(r,cf.sv,fnt,ccol);}
}
frame.clip=oc;
if(!x.locked&&sel&& ((ev.dclick&&over(cell)) || (ev.action&&x.bycell&&z==value->col&&y+value->scroll==display_row(value->row)))){
char f=z<fk?x.format[z]:'s';pair tc=(pair){z,y+value->scroll},tpc=(pair){tc.x,permuted_row(tc.y)};
if (f=='I'||f=='L'||f=='T'){} // no editing allowed
else if(f=='B'||f=='b'){grid_edit_cell(tpc,ls(lmn(!lb(v))));} // toggle
else{
ms.pending_grid_cell_rich=f=='t';
wid.pending_grid_edit=1,ms.pending_grid_cell=grid_cell(x,value,bb,(pair){tc.x,tc.y-value->scroll},cw);
ms.pending_grid_cell.y-=1,ms.pending_grid_cell.w+=1,ms.pending_grid_cell.h+=2;
ms.cell=tpc;ms.text=(field_val){f=='t'?rtext_cast(v):rtext_cast(lmcstr(cf.sv)),0};
}
}
}
}
if(x.lines)for(int y=1;y<nrd;y++)draw_hline(bb.x,bb.x+bb.w,bb.y+rh*y,fcol);
if(!x.locked&&in_layer()&&target)for(int z=0,cx=bh.x;z<nc;cx+=cw[z],z++){
if(cw[z]==0)continue;
rect h={cx+cw[z]-1,bh.y,5,bh.h};if(h.x+h.w>b.x+b.w)break;
if(over(h))draw_vline(h.x+2,h.y,h.y+h.h,13);
if(ev.md&&dover(h)){wid.col_drag=1,wid.col_num=z,wid.col_orig=cw[z];}
if(sel&&wid.col_drag&&wid.col_num==z&&ev.drag){
int s=MIN(MAX(10,wid.col_orig+(ev.pos.x-ev.dpos.x)),bb.w-10),i=z;uicursor=cursor_drag;
GEN(r,MAX(x.widths[0],i+1))lmn(i==z?s:cw[z]);iwrite(target,lmistr("widths"),r),mark_dirty();
}
}
if(target&&os!=value->scroll)iwrite(target,lmistr("scroll"),lmn(value->scroll)),mark_dirty();
if(target&&or!=value->row)iwrite(target,lmistr("row"),lmn(value->row)),mark_dirty();
if(target&&oc!=value->col)iwrite(target,lmistr("col"),lmn(value->col)),mark_dirty();
if(target&&clicked)msg.target_click=target,msg.arg_click=(fpair){0,value->row};
if(in_widgets())wid.count++;
return files?((clicked&&ev.dclick)||(sel&&ev.action)): clicked;
}Comment by Insanity 1 day ago
I guess LLMs would do a reasonable job making this legible, as long as there can be some tests to validate functionality alongside the migration.
Comment by supermaurio2000 1 day ago
Comment by LooseMarmoset 1 day ago
https://www.roguebasin.com/index.php?title=Decker#Screenshot...
Comment by supermaurio2000 1 day ago
Comment by rpdillon 22 hours ago
> Decker has never been developed by or with the aid of any form of LLM or “genai” tool. I have no intention of using them at any point in the future. Decker’s primary external dependency is SDL, which has a firm anti-LLM contribution policy, and I will do my best to avoid or gradually replace any dependencies which incorporate slop in the future. Slop-free browsers and operating systems pose a larger challenge, but Native-Decker does support netBSD and Haiku.
It's a bit too dogmatic for my taste, and I wonder how it will age, but it's also totally reasonable to say "This is my garden, please don't propose changes that use a backhoe and dynamite".
Comment by jhbadger 22 hours ago
Comment by hexjy 1 day ago