local gfx=require("yaru.gfx") local unicode=require("unicode") local vgpu={} local color=gfx.color local remapFg={[0x000000]=0x171421,[0xFF0000]=0xF66151,[0x00FF00]=0x33DA7A,[0xFFFF00]=0xE9AD0C, [0x0000FF]=0x2A7BDE,[0xFF00FF]=0xC061CB,[0x00B6FF]=0x33C7DE,[0xFFFFFF]=0xFFFFFF} function vgpu.new(w,h,opts) opts=opts or{} local termBg=opts.bg or 0x2C001E local g={w=w,h=h,grid={},changed=true,scroll=0} local fg,bg=0xFFFFFF,0x000000 local fgK,bgK local palette={} for i=0,15 do palette[i]=(i*17)<<16|(i*17)<<8|(i*17)end local function mapFg(c)return remapFg[c]or c end local function mapBg(c)if c==0 then return termBg end return c end local function upd()fgK=color(mapFg(fg))<<21;bgK=color(mapBg(bg))<<29 end upd() local grid=g.grid local blank=function()return 32|fgK|bgK end for i=1,w*h do grid[i]=32|fgK|bgK end local function touch()if not g.changed then g.changed=true;if g.onChange then g.onChange()end end end local p={type="gpu",address=opts.address or"vgpu"} g.proxy=p function p.getScreen()return opts.screen end function p.bind()return true end function p.getResolution()return g.w,g.h end function p.maxResolution()return g.w,g.h end function p.getViewport()return g.w,g.h end function p.setResolution(nw,nh) if nw==g.w and nh==g.h then return false end if g.onResize then return g.onResize(nw,nh)end return false end p.setViewport=p.setResolution function p.getDepth()return 8 end function p.maxDepth()return 8 end function p.setDepth()return 8 end function p.getForeground()return fg,false end function p.getBackground()return bg,false end function p.setForeground(c,isPal) local old=fg if isPal then c=palette[c]or 0xFFFFFF end fg=c;upd();return old end function p.setBackground(c,isPal) local old=bg if isPal then c=palette[c]or 0 end bg=c;upd();return old end function p.getPaletteColor(i)return palette[i]end function p.setPaletteColor(i,c)local o=palette[i];palette[i]=c;return o end function p.set(x,y,s,vertical) x,y=math.floor(x),math.floor(y) local W,H=g.w,g.h if not vertical and(y<1 or y>H)then return true end local i=0 local k=fgK|bgK for _,cp in utf8.codes(s)do local xx,yy=x+i,y if vertical then xx,yy=x,y+i end if xx>=1 and xx<=W and yy>=1 and yy<=H then grid[(yy-1)*W+xx]=cp|k end i=i+1 end touch() return true end function p.get(x,y) x,y=math.floor(x),math.floor(y) if x<1 or y<1 or x>g.w or y>g.h then error("index out of bounds")end local v=grid[(y-1)*g.w+x] return unicode.char(v&0x1FFFFF),gfx.rgb((v>>21)&255),gfx.rgb((v>>29)&255) end function p.fill(x,y,fw,fh,ch) x,y,fw,fh=math.floor(x),math.floor(y),math.floor(fw),math.floor(fh) local W=g.w local v=utf8.codepoint(ch)|fgK|bgK local x2,y2=math.min(W,x+fw-1),math.min(g.h,y+fh-1) x,y=math.max(1,x),math.max(1,y) for yy=y,y2 do local base=(yy-1)*W for xx=x,x2 do grid[base+xx]=v end end touch() return true end function p.copy(x,y,cw,ch,tx,ty) x,y,cw,ch,tx,ty=math.floor(x),math.floor(y),math.floor(cw),math.floor(ch),math.floor(tx),math.floor(ty) local W,H=g.w,g.h local x1,y1=math.max(1,x),math.max(1,y) local x2,y2=math.min(W,x+cw-1),math.min(H,y+ch-1) if x1>x2 or y1>y2 then return true end local rows={} local ystart,ystop,ystep=y1,y2,1 if ty>0 then ystart,ystop,ystep=y2,y1,-1 end local xstart,xstop,xstep=x1,x2,1 if tx>0 then xstart,xstop,xstep=x2,x1,-1 end for yy=ystart,ystop,ystep do local dy=yy+ty if dy>=1 and dy<=H then local sb,db=(yy-1)*W,(dy-1)*W for xx=xstart,xstop,xstep do local dx=xx+tx if dx>=1 and dx<=W then grid[db+dx]=grid[sb+xx]end end end end if tx==0 and ty<0 and x1==1 and x2==W and y1+ty==1 and y2==H then g.scroll=g.scroll-ty else g.noscroll=true end touch() return true end function p.getActiveBuffer()return 0 end function p.setActiveBuffer()return 0 end function p.buffers()return{}end function p.allocateBuffer()return nil,"not enough video memory"end function p.freeBuffer()return false end function p.freeAllBuffers()end function p.totalMemory()return 0 end function p.freeMemory()return 0 end function p.bitblt()return false end function g.resize(nw,nh) local ng={} local b=blank() for yy=1,nh do for xx=1,nw do ng[(yy-1)*nw+xx]=(xx<=g.w and yy<=g.h)and grid[(yy-1)*g.w+xx]or b end end for i=1,#grid do grid[i]=nil end for i=1,nw*nh do grid[i]=ng[i]end g.w,g.h=nw,nh g.noscroll=true touch() end function g.setTermBg(c)termBg=c;upd()end return g end return vgpu