]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vmx/vga.c
ndb/dnsquery, ndb/csquery: handle long lines
[plan9front.git] / sys / src / cmd / vmx / vga.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <thread.h>
5 #include <draw.h>
6 #include <cursor.h>
7 #include <mouse.h>
8 #include <keyboard.h>
9 #include <ctype.h>
10 #include "dat.h"
11 #include "fns.h"
12
13 static uchar *fb, *tfb;
14 uintptr fbsz;
15 uintptr fbaddr;
16
17 VgaMode *curmode, *nextmode, *modes, **modeslast = &modes;
18 int curhbytes, nexthbytes;
19 int vesamode, maxw, maxh;
20 int novga;
21
22 VgaMode textmode = {
23         .w 640, .h 400, .no 3
24 };
25
26 static Image *img, *bg;
27 static Mousectl *mc;
28 static Rectangle picr;
29 Channel *kbdch, *mousech;
30 u8int mousegrab;
31 extern u8int mouseactive;
32 static uchar *sfb;
33
34 typedef struct VGA VGA;
35 struct VGA {
36         u8int miscout;
37         u8int cidx; /* crtc */
38         u8int aidx; /* attribute (bit 7: access flipflop) */
39         u8int gidx; /* graphics */
40         u8int sidx; /* sequencer */
41         u16int rdidx, wdidx; /* bit 0-1: color */
42         u8int attr[32];
43         u8int seq[5];
44         u8int graph[9];
45         u32int pal[256];
46         Image *col[256];
47         Image *acol[16];
48         u8int crtc[0x18];
49         QLock;
50 } vga = {
51         .miscout 1,
52         .attr { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
53         .crtc { [10] 13, [11] 14 },
54         .pal {
55                 0x000000ff, 0x0000a8ff, 0x00a800ff, 0x00a8a8ff, 0xa80000ff, 0xa800a8ff, 0xa85400ff, 0xa8a8a8ff,
56                 0x545454ff, 0x5454fcff, 0x54fc54ff, 0x54fcfcff, 0xfc5454ff, 0xfc54fcff, 0xfcfc54ff, 0xfcfcfcff,
57                 0x000000ff, 0x141414ff, 0x202020ff, 0x2c2c2cff, 0x383838ff, 0x444444ff, 0x505050ff, 0x606060ff,
58                 0x707070ff, 0x808080ff, 0x909090ff, 0xa0a0a0ff, 0xb4b4b4ff, 0xc8c8c8ff, 0xe0e0e0ff, 0xfcfcfcff,
59                 0x0000fcff, 0x4000fcff, 0x7c00fcff, 0xbc00fcff, 0xfc00fcff, 0xfc00bcff, 0xfc007cff, 0xfc0040ff,
60                 0xfc0000ff, 0xfc4000ff, 0xfc7c00ff, 0xfcbc00ff, 0xfcfc00ff, 0xbcfc00ff, 0x7cfc00ff, 0x40fc00ff,
61                 0x00fc00ff, 0x00fc40ff, 0x00fc7cff, 0x00fcbcff, 0x00fcfcff, 0x00bcfcff, 0x007cfcff, 0x0040fcff,
62                 0x7c7cfcff, 0x9c7cfcff, 0xbc7cfcff, 0xdc7cfcff, 0xfc7cfcff, 0xfc7cdcff, 0xfc7cbcff, 0xfc7c9cff,
63                 0xfc7c7cff, 0xfc9c7cff, 0xfcbc7cff, 0xfcdc7cff, 0xfcfc7cff, 0xdcfc7cff, 0xbcfc7cff, 0x9cfc7cff,
64                 0x7cfc7cff, 0x7cfc9cff, 0x7cfcbcff, 0x7cfcdcff, 0x7cfcfcff, 0x7cdcfcff, 0x7cbcfcff, 0x7c9cfcff,
65                 0xb4b4fcff, 0xc4b4fcff, 0xd8b4fcff, 0xe8b4fcff, 0xfcb4fcff, 0xfcb4e8ff, 0xfcb4d8ff, 0xfcb4c4ff,
66                 0xfcb4b4ff, 0xfcc4b4ff, 0xfcd8b4ff, 0xfce8b4ff, 0xfcfcb4ff, 0xe8fcb4ff, 0xd8fcb4ff, 0xc4fcb4ff,
67                 0xb4fcb4ff, 0xb4fcc4ff, 0xb4fcd8ff, 0xb4fce8ff, 0xb4fcfcff, 0xb4e8fcff, 0xb4d8fcff, 0xb4c4fcff,
68                 0x000070ff, 0x1c0070ff, 0x380070ff, 0x540070ff, 0x700070ff, 0x700054ff, 0x700038ff, 0x70001cff,
69                 0x700000ff, 0x701c00ff, 0x703800ff, 0x705400ff, 0x707000ff, 0x547000ff, 0x387000ff, 0x1c7000ff,
70                 0x007000ff, 0x00701cff, 0x007038ff, 0x007054ff, 0x007070ff, 0x005470ff, 0x003870ff, 0x001c70ff,
71                 0x383870ff, 0x443870ff, 0x543870ff, 0x603870ff, 0x703870ff, 0x703860ff, 0x703854ff, 0x703844ff,
72                 0x703838ff, 0x704438ff, 0x705438ff, 0x706038ff, 0x707038ff, 0x607038ff, 0x547038ff, 0x447038ff,
73                 0x387038ff, 0x387044ff, 0x387054ff, 0x387060ff, 0x387070ff, 0x386070ff, 0x385470ff, 0x384470ff,
74                 0x505070ff, 0x585070ff, 0x605070ff, 0x685070ff, 0x705070ff, 0x705068ff, 0x705060ff, 0x705058ff,
75                 0x705050ff, 0x705850ff, 0x706050ff, 0x706850ff, 0x707050ff, 0x687050ff, 0x607050ff, 0x587050ff,
76                 0x507050ff, 0x507058ff, 0x507060ff, 0x507068ff, 0x507070ff, 0x506870ff, 0x506070ff, 0x505870ff,
77                 0x000040ff, 0x100040ff, 0x200040ff, 0x300040ff, 0x400040ff, 0x400030ff, 0x400020ff, 0x400010ff,
78                 0x400000ff, 0x401000ff, 0x402000ff, 0x403000ff, 0x404000ff, 0x304000ff, 0x204000ff, 0x104000ff,
79                 0x004000ff, 0x004010ff, 0x004020ff, 0x004030ff, 0x004040ff, 0x003040ff, 0x002040ff, 0x001040ff,
80                 0x202040ff, 0x282040ff, 0x302040ff, 0x382040ff, 0x402040ff, 0x402038ff, 0x402030ff, 0x402028ff,
81                 0x402020ff, 0x402820ff, 0x403020ff, 0x403820ff, 0x404020ff, 0x384020ff, 0x304020ff, 0x284020ff,
82                 0x204020ff, 0x204028ff, 0x204030ff, 0x204038ff, 0x204040ff, 0x203840ff, 0x203040ff, 0x202840ff,
83                 0x2c2c40ff, 0x302c40ff, 0x342c40ff, 0x3c2c40ff, 0x402c40ff, 0x402c3cff, 0x402c34ff, 0x402c30ff,
84                 0x402c2cff, 0x40302cff, 0x40342cff, 0x403c2cff, 0x40402cff, 0x3c402cff, 0x34402cff, 0x30402cff,
85                 0x2c402cff, 0x2c4030ff, 0x2c4034ff, 0x2c403cff, 0x2c4040ff, 0x2c3c40ff, 0x2c3440ff, 0x2c3040ff,
86                 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff,
87         },
88 };
89
90 static void
91 newpal(int l, int n, int dofree)
92 {
93         int x;
94
95         assert(l >= 0 && n + l <= 256);
96         for(; n-- > 0; l++){
97                 if(dofree)
98                         freeimage(vga.col[l]);
99                 vga.col[l] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, vga.pal[l]);
100                 if(vga.col[l] == nil) sysfatal("allocimage: %r");
101         }
102         for(l = 0; l < 16; l++){
103                 x = vga.attr[0x14] << 4 & 0xc0 | vga.attr[l] & 0x3f;
104                 if((vga.attr[0x10] & 0x80) != 0)
105                         x = x & 0xcf | vga.attr[0x14] << 4 & 0x30;
106                 vga.acol[l] = vga.col[x];
107         }
108 }
109
110 u32int
111 vgagetpal(u8int n)
112 {
113         return vga.pal[n];
114 }
115
116 void
117 vgasetpal(u8int n, u32int v)
118 {
119         qlock(&vga);
120         vga.pal[n] = v;
121         newpal(n, 1, 1);
122         qunlock(&vga);
123 }
124
125 static void
126 screeninit(int resize)
127 {
128         Point p;
129         int ch;
130
131         if(!resize)
132                 freeimage(img);
133         else{
134                 bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
135                 newpal(0, 256, 0);
136         }
137         p = divpt(addpt(screen->r.min, screen->r.max), 2);
138         picr = (Rectangle){subpt(p, Pt(curmode->w/2, curmode->h/2)), addpt(p, Pt((curmode->w+1)/2, (curmode->h+1)/2))};
139         switch(curmode->chan){
140         case 0: ch = screen->chan; break;
141         case CHAN1(CMap, 4): case CMAP8:
142                 if(vesamode){
143                         ch = RGBA32;
144                         break;
145                 }
146                 /* wet floor */
147         default: ch = curmode->chan; break;
148         }
149         img = allocimage(display, Rect(0, 0, curmode->w, curmode->h), ch, 0, 0);
150         draw(screen, screen->r, bg, nil, ZP);
151 }
152
153 u32int
154 vgaio(int isin, u16int port, u32int val, int sz, void *)
155 {
156         u32int m;
157
158         if(novga)
159                 return 0;
160         if(port != 0x3df && sz == 2 && !isin){
161                 vgaio(0, port, (u8int)val, 1, nil);
162                 return vgaio(0, port+1, (u8int)(val >> 8), 1, nil);
163         }
164         if(sz != 1) vmdebug("vga: non-byte access to port %#ux, sz=%d", port, sz);
165         val = (u8int) val;
166         switch(isin << 16 | port){
167         case 0x3c0:
168                 if((vga.aidx & 0x80) != 0){
169                         vmdebug("vga: attribute write %#.2x = %#.2x", vga.aidx & 0x1f, val);
170                         vga.attr[vga.aidx & 0x1f] = val;
171                         qlock(&vga); newpal(0, 0, 0); qunlock(&vga);
172                 }else
173                         vga.aidx = val & 0x3f;
174                 vga.aidx ^= 0x80;
175                 return 0;
176         case 0x3c2: vga.miscout = val; return 0;
177         case 0x3c4: vga.sidx = val; return 0;
178         case 0x3c5:
179                 switch(vga.sidx){
180                 case 0: vga.seq[vga.sidx] = val & 3; return 0;
181                 case 4: vga.seq[vga.sidx] = val & 0xe; return 0;
182                 default: vmerror("vga: write to unknown sequencer register %#ux (val=%#ux)", vga.sidx, val); return 0;
183                 }
184         case 0x3c6: return 0;
185         case 0x3c7: vga.rdidx = val << 2; return 0;
186         case 0x3c8: vga.wdidx = val << 2; return 0;
187         case 0x3c9:
188                 vga.pal[vga.wdidx >> 2] = vga.pal[vga.wdidx >> 2] & ~(0xff << (~vga.wdidx << 3 & 24)) | val << 2 + (~vga.wdidx << 3 & 24);
189                 qlock(&vga); newpal(vga.wdidx >> 2, 1, 1); qunlock(&vga);
190                 vga.wdidx = vga.wdidx + 1 + (vga.wdidx >> 1 & 1) & 0x3ff;
191                 return 0;
192         case 0x3ce: vga.gidx = val; return 0;
193         case 0x3cf:
194                 switch(vga.gidx){
195                 case 4: vga.graph[vga.gidx] = val & 3; break;
196                 case 8: vga.graph[vga.gidx] = val; break;
197                 default:
198                         vmerror("vga: write to unknown graphics register %#ux (val=%#ux)", vga.gidx, val);
199                 }
200                 return 0;
201         case 0x3d4: vga.cidx = val; return 0;
202         case 0x3d5:
203                 switch(vga.cidx){
204                 case 10: case 11: case 12: case 13: case 14: case 15:
205                         vga.crtc[vga.cidx] = val;
206                         return 0;
207                 default:
208                         vmerror("vga: write to unknown CRTC register %#ux (val=%#ux)", vga.cidx, val);
209                 }
210                 return 0;
211         case 0x103c0: return vga.aidx & 0x3f;
212         case 0x103c1: return vga.attr[vga.aidx & 0x1f];
213         case 0x103c4: return vga.sidx;
214         case 0x103c5:   
215                 switch(vga.sidx){
216                 case 0:
217                 case 4:
218                         return vga.seq[vga.sidx];
219                 default: vmerror("vga: read from unknown sequencer register %#ux (val=%#ux)", vga.sidx, val); return 0;
220                 }
221         case 0x103c6: return 0xff;
222         case 0x103c7: return vga.rdidx >> 2;
223         case 0x103c8: return vga.wdidx >> 2;
224         case 0x103c9:
225                 m = vga.pal[vga.rdidx >> 2] >> (~vga.rdidx << 3 & 24) + 2;
226                 vga.rdidx = vga.rdidx + 1 + (vga.rdidx >> 1 & 1) & 0x3ff;
227                 return m;
228         case 0x103cc: return vga.miscout;
229         case 0x103ce: return vga.gidx;
230         case 0x103cf:
231                 switch(vga.gidx){
232                 case 4:
233                 case 8:
234                         return vga.graph[vga.gidx];
235                 default:
236                         vmerror("vga: read from unknown graphics register %#ux", vga.gidx);
237                         return 0;
238                 }
239         case 0x103d4: return vga.cidx;
240         case 0x103d5:
241                 switch(vga.cidx){
242                 case 10: case 11: case 12: case 13: case 14: case 15:
243                         return vga.crtc[vga.cidx];
244                 default:
245                         vmerror("vga: read from unknown CRTC register %#ux", vga.cidx);
246                         return 0;
247                 }
248         case 0x103ca:
249         case 0x103da:
250                 vga.aidx &= 0x7f;
251                 return 0;
252         }
253         return iowhine(isin, port, val, sz, "vga");
254 }
255
256 typedef struct Key Key;
257 struct Key {
258         Rune r;
259         int code;
260         Key *next;
261 };
262 Key *kbdmap[128];
263
264 static void
265 defkey(Rune r, int code)
266 {
267         Key *k, **kp;
268
269         for(kp = &kbdmap[r % nelem(kbdmap)]; *kp != nil; kp = &(*kp)->next)
270                 if((*kp)->r == r)
271                         return;
272         k = emalloc(sizeof(Key));
273         k->r = r;
274         k->code = code;
275         *kp = k;
276 }
277
278 void
279 kbdlayout(char *fn)
280 {
281         Biobuf *bp;
282         char *s, *p, *f[10];
283         int nf, x, y;
284         Rune z;
285         
286         defkey(Kshift, 0x2a);
287         defkey(Kctl, 0x1d);
288         defkey(Kalt, 0x38);
289         defkey(Kctl, 0x11d);
290         defkey(Kprint, 0x137);
291         defkey(Kaltgr, 0x138);
292         defkey(Kbreak, 0x146);
293         defkey(Khome, 0x147);
294         defkey(Kup, 0x148);
295         defkey(Kpgup, 0x149);
296         defkey(Kleft, 0x14b);
297         defkey(Kright, 0x14d);
298         defkey(Kend, 0x14f);
299         defkey(Kdown, 0x150);
300         defkey(Kpgdown, 0x151);
301         defkey(Kins, 0x152);
302         defkey(Kdel, 0x153);
303         defkey(Kup, 0x179);
304
305         bp = Bopen(fn, OREAD);
306         if(bp == nil){
307                 vmerror("kbdlayout: %r");
308                 return;
309         }
310         for(;; free(s)){
311                 s = Brdstr(bp, '\n', 1);
312                 if(s == nil) break;
313                 nf = getfields(s, f, nelem(f), 1, " \t");
314                 if(nf < 3) continue;
315                 x = strtol(f[0], &p, 0);
316                 if(*p != 0) continue;
317                 y = strtol(f[1], &p, 0);
318                 if(*p != 0) continue;
319                 if(*f[2] == '\'' || *f[2] == '^'){
320                         chartorune(&z, f[2]+1);
321                         if(*f[2] == '^') z -= '@';
322                 }else{
323                         z = strtol(f[2], &p, 0);
324                         if(*p != 0) continue;
325                 }
326                 
327                 if(x != 0 || z == 0) continue;
328                 defkey(z, y);
329         }
330         Bterm(bp);
331 }
332
333 static vlong kbwatchdog; /* used to release mouse grabbing if keyproc is stuck */
334
335 void
336 keyproc(void *)
337 {
338         int fd, n;
339         static char buf[256];
340         static uvlong kdown[8], nkdown[8];
341         uvlong set, rls;
342         int i, j;
343         char *s;
344         Rune r;
345         Key *k;
346
347         threadsetname("keyproc");
348         fd = open("/dev/kbd", OREAD);
349         if(fd < 0)
350                 sysfatal("open: %r");
351         for(;;){
352                 if(buf[0] != 0){
353                         n = strlen(buf)+1;
354                         memmove(buf, buf+n, sizeof(buf)-n);
355                 }
356                 if(buf[0] == 0){
357                         kbwatchdog = 0;
358                         n = read(fd, buf, sizeof(buf)-1);
359                         if(n <= 0)
360                                 sysfatal("read /dev/kbd: %r");
361                         kbwatchdog = nsec();
362                         buf[n-1] = 0;
363                         buf[n] = 0;
364                 }
365                 if(buf[0] != 'k' && buf[0] != 'K')
366                         continue;
367                 s = buf + 1;
368                 memset(nkdown, 0, sizeof(nkdown));
369                 while(*s != 0){
370                         s += chartorune(&r, s);
371                         for(k = kbdmap[r % nelem(kbdmap)]; k != nil; k = k->next)
372                                 if(k->r == r){
373                                         nkdown[k->code >> 6] |= 1ULL<<(k->code&63);
374                                         break;
375                                 }
376                         if(k == nil) vmerror("unknown key %d", r);
377                 }
378                 if(mousegrab && (nkdown[0]>>29 & 1) != 0 && (nkdown[0]>>56 & 1) != 0){
379                         mousegrab = 0;
380                         setcursor(mc, nil);
381                 }
382                 for(i = 0; i < 8; i++){
383                         if(nkdown[i] == kdown[i]) continue;
384                         set = nkdown[i] & ~kdown[i];
385                         rls = ~nkdown[i] & kdown[i];
386                         for(j = 0; j < 64; j++, set>>=1, rls >>= 1)
387                                 if(((set|rls) & 1) != 0){
388                                         if(i >= 4)
389                                                 sendul(kbdch, 0xe0);
390                                         sendul(kbdch, j | i<<6&0xff | ((rls&1) != 0 ? 0x80 : 0));
391                                         sendnotif(i8042kick, nil);
392                                 }
393                         kdown[i] = nkdown[i];
394                 }
395         }
396 }
397
398 void
399 mousethread(void *)
400 {
401         Mouse m;
402         static Mouse mm, om;
403         int gotm;
404         Point mid;
405         Rectangle grabout;
406         int clicked;
407         static Cursor blank;
408         
409         gotm = 0;
410         clicked = 0;
411         for(;;){
412                 Alt a[] = {
413                         {mc->c, &m, CHANRCV},
414                         {mousech, &mm, gotm ? CHANSND : CHANNOP},
415                         {nil, nil, CHANEND},
416                 };
417                 
418                 switch(alt(a)){
419                 case 0:
420                         mid = divpt(addpt(picr.max, picr.min), 2);
421                         grabout = insetrect(Rpt(mid, mid), -50);
422                         if(!ptinrect(m.xy, picr)){
423                                 clicked = 0;
424                                 break;
425                         }
426                         if(!mousegrab){
427                                 if(clicked && (m.buttons & 1) == 0 && mouseactive){
428                                         mousegrab = 1;
429                                         setcursor(mc, &blank);
430                                 }
431                                 clicked = m.buttons & 1;
432                                 break;
433                         }
434                         if(kbwatchdog != 0 && nsec() - kbwatchdog > 1000ULL*1000*1000)
435                                 mousegrab = 0;
436                         gotm = 1;
437                         if(!ptinrect(m.xy, grabout)){
438                                 moveto(mc, mid);
439                                 m.xy = mid;
440                                 om.xy = mid;
441                         }
442                         mm.xy = addpt(mm.xy, subpt(m.xy, om.xy));
443                         om = m;
444                         mm.buttons = m.buttons;
445                         break;
446                 case 1:
447                         sendnotif(i8042kick, nil);
448                         mm.xy = Pt(0,0);
449                         gotm = 0;
450                         break;
451                 }
452         }
453 }
454
455 static Rune cp437[256] = {
456         0x0020, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,
457         0x25ba, 0x25c4, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc, 
458         0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 
459         0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 
460         0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 
461         0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 
462         0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 
463         0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, 
464         0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 
465         0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, 
466         0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 
467         0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 
468         0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 
469         0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 
470         0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, 
471         0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0, 
472 };
473
474 static void
475 drawtext(void)
476 {
477         Rune buf[80];
478         uchar *p, attr;
479         int y, x, x1;
480         Rectangle r;
481         u16int cp;
482         static uchar rbuf[80*25*2];
483         u16int sa;
484         
485         sa = vga.crtc[12] << 8 | vga.crtc[13];
486         if(sa + 80*25 >= 0x10000){
487                 memset(rbuf, 0, sizeof(rbuf));
488                 memmove(rbuf, tfb + sa * 2, 0x10000 - 80*25 - sa);
489                 p = rbuf;
490         }else
491                 p = tfb + sa * 2;
492         for(y = 0; y < 25; y++){
493                 for(x = 0; x < 80; x++)
494                         buf[x] = cp437[p[2*x]];
495                 for(x = 0; x < 80; x = x1){
496                         attr = p[2*x+1];
497                         for(x1 = x; x1 < 80 && p[2*x1+1] == attr; x1++)
498                                 ;
499                         r = Rect(x * 8, y * 16, x1 * 8, (y + 1) * 16);
500                         draw(img, r, vga.acol[attr >> 4], nil, ZP);
501                         runestringn(img, r.min, vga.acol[attr & 0xf], ZP, display->defaultfont, buf + x, x1 - x);
502                 }
503                 p += 160;
504         }
505         cp = (vga.crtc[14] << 8 | vga.crtc[15]);
506         if(cp >= sa && cp < sa + 80*25 && (vga.crtc[10] & 0x20) == 0 && nsec() / 500000000 % 2 == 0){
507                 buf[0] = cp437[tfb[cp*2]];
508                 attr = tfb[cp*2+1];
509                 r.min = Pt((cp - sa) % 80 * 8, (cp - sa) / 80 * 16);
510                 r.max = Pt(r.min.x + 8, r.min.y + (vga.crtc[11] & 0x1f) + 1);
511                 r.min.y += vga.crtc[10] & 0x1f;
512                 draw(img, r, vga.acol[attr & 0xf], nil, ZP);
513         }
514         draw(screen, picr, img, nil, ZP);
515         flushimage(display, 1); 
516 }
517
518 static void
519 drawfb(int redraw)
520 {
521         u32int *p, *q;
522         Rectangle upd;
523         int xb, y, hb;
524         u32int v;
525         uchar *cp;
526         u32int *buf, *bp;
527
528         p = (u32int *) fb;
529         q = (u32int *) sfb;
530         upd.min.y = upd.max.y = -1;
531         xb = 0;
532         y = 0;
533         hb = curhbytes;
534         while(p < (u32int*)(fb + curmode->sz)){
535                 if(*p != *q || redraw){
536                         if(upd.min.y < 0) upd.min.y = y;
537                         upd.max.y = y + 1 + (xb + 4 > hb);
538                         *q = *p;
539                 }
540                 p++;
541                 q++;
542                 xb += 4;
543                 if(xb >= hb){
544                         xb -= hb;
545                         y++;
546                 }
547         }
548         if(upd.min.y == upd.max.y) return;
549         upd.min.x = 0;
550         upd.max.x = curmode->w;
551         if(vesamode && (curmode->chan >> 4 == CMap)){
552                 buf = emalloc(curmode->w * 4 * (upd.max.y - upd.min.y));
553                 bp = buf;
554                 for(y = upd.min.y; y < upd.max.y; y++){
555                         cp = sfb + y * hb;
556                         for(xb = 0; xb < curmode->w; xb++){
557                                 if(curmode->chan == CMAP8)
558                                         v = *cp++;
559                                 else if((xb & 1) == 0)
560                                         v = *cp & 0xf;
561                                 else
562                                         v = *cp++ >> 4;
563                                 *bp++ = vga.pal[v];
564                         }
565                 }
566                 loadimage(img, upd, (void *) buf, curmode->w * 4 * (upd.max.y - upd.min.y));
567                 free(buf);
568                 draw(screen, rectaddpt(upd, picr.min), img, nil, upd.min);
569         }else if(curmode->chan != screen->chan || !rectinrect(picr, screen->r)){
570                 if(curmode->hbytes != hb){
571                         for(y = upd.min.y; y < upd.max.y; y++)
572                                 loadimage(img, Rect(0, y, curmode->w, y+1), sfb + y * hb, curmode->hbytes);
573                 }else
574                         loadimage(img, upd, sfb + upd.min.y * hb, (upd.max.y - upd.min.y) * hb);
575                 draw(screen, rectaddpt(upd, picr.min), img, nil, upd.min);
576         }else{
577                 if(curmode->hbytes != hb){
578                         for(y = upd.min.y; y < upd.max.y; y++)
579                                 loadimage(screen, Rect(picr.min.x, picr.min.y + y, picr.max.x, picr.min.y + y + 1), sfb + y * hb, curmode->hbytes);
580                 }else
581                         loadimage(screen, rectaddpt(upd, picr.min), sfb + upd.min.y * hb, (upd.max.y - upd.min.y) * hb);
582         }
583         flushimage(display, 1);
584 }
585
586 void
587 drawproc(void *)
588 {
589         ulong ul;
590         int event;
591         VgaMode *m;
592
593         threadsetname("draw");
594         sfb = emalloc(fbsz);
595         event = 4;
596         for(;; sleep(20)){
597                 qlock(&vga);
598                 m = nextmode;
599                 if(m != curmode){
600                         event |= 1;
601                         curmode = m;
602                         curhbytes = m->hbytes;
603                 }
604                 if(nexthbytes != curhbytes){
605                         event |= 1;
606                         curhbytes = nexthbytes;
607                 }
608                 while(nbrecv(mc->resizec, &ul) > 0)
609                         event |= 2;
610                 if((event & 3) != 0){
611                         if((event & 2) != 0 && getwindow(display, Refnone) < 0)
612                                 sysfatal("resize failed: %r");
613                         screeninit((event & 2) != 0);
614                 }
615                 if(curmode == &textmode)
616                         drawtext();
617                 else
618                         drawfb(event != 0);
619                 event = 0;
620                 qunlock(&vga);
621         }
622 }
623
624 static int
625 chancheck(u32int ch)
626 {
627         u8int got;
628         int i, t;
629         
630         got = 0;
631         for(i = 0; i < 4; i++){
632                 t = ch >> 8 * i + 4 & 15;
633                 if((ch >> 8 * i & 15) == 0) continue;
634                 if(t >= NChan) return 0;
635                 if((got & 1<<t) != 0) return 0;
636                 got |= 1<<t;
637         }
638         if(!vesamode) return 1;
639         switch(got){
640         case 1<<CRed|1<<CGreen|1<<CBlue:
641         case 1<<CRed|1<<CGreen|1<<CBlue|1<<CAlpha:
642         case 1<<CRed|1<<CGreen|1<<CBlue|1<<CIgnore:
643                 return 1;
644         case 1<<CMap:
645                 return chantodepth(ch) == 4 || chantodepth(ch) == 8;
646         default:
647                 return 0;
648         }
649 }
650
651 static char *
652 vgamodeparse(char *p, VgaMode **mp)
653 {
654         char *r;
655         VgaMode *m;
656         char c;
657         
658         m = emalloc(sizeof(VgaMode));
659         *mp = m;
660         *modeslast = m;
661         modeslast = &m->next;
662         m->w = strtoul(p, &r, 10);
663         if(*r != 'x')
664         nope:
665                 sysfatal("invalid mode specifier");
666         p = r + 1;
667         m->h = strtoul(p, &r, 10);
668         if(*r != 'x'){
669                 m->chan = XRGB32;
670                 goto out;
671         }
672         p = r + 1;
673         while(isalnum(*r))
674                 r++;
675         c = *r;
676         *r = 0;
677         m->chan = strtochan(p);
678         *r = c;
679         if(m->chan == 0 || !chancheck(m->chan))
680                 goto nope;
681 out:
682         if(m->w > maxw) maxw = m->w;
683         if(m->h > maxh) maxh = m->h;
684         return r;
685 }
686
687 void
688 vgafbparse(char *fbstring)
689 {
690         char *p, *q;
691         VgaMode *m;
692
693         if(strcmp(fbstring, "text") == 0){
694                 curmode = &textmode;
695                 maxw = 640;
696                 maxh = 400;
697                 return;
698         }else if(strncmp(fbstring, "vesa:", 5) == 0){
699                 vesamode = 1;
700                 p = fbstring + 5;
701         }else
702                 p = fbstring;
703         do{
704                 q = vgamodeparse(p, &m);
705                 if(p == q || m->w <= 0 || m->h <= 0)
706                         no: sysfatal("invalid mode specifier");
707                 m->hbytes = chantodepth(m->chan) * m->w + 7 >> 3;
708                 m->sz = m->hbytes * m->h;
709                 if(m->sz > fbsz) fbsz = m->sz;
710                 p = q;
711         }while(*p++ == ',');
712         if(*--p == '@'){
713                 p++;
714                 fbaddr = strtoul(p, &q, 0);
715                 if(p == q) goto no;
716                 p = q;
717         }else
718                 fbaddr = 0xf0000000;
719         if(*p != 0) goto no;
720         if(modes == nil || vesamode == 0 && modes->next != nil)
721                 goto no;
722         if(vesamode == 0){
723                 curmode = modes;
724                 curhbytes = curmode->hbytes;
725                 fbsz = -(-fbsz & -4096);
726                 novga = 1;
727         }else{
728                 curmode = &textmode;
729                 if(fbsz < (1<<22))
730                         fbsz = 1<<22;
731                 else
732                         fbsz = roundpow2(fbsz);
733         }
734 }
735
736
737 void
738 vgainit(void)
739 {
740         char buf[512];
741         int i;
742         PCIDev *d;
743         extern void vesainit(void);
744
745         if(curmode == nil) return;
746         nextmode = curmode;
747         nexthbytes = curhbytes;
748         tfb = gptr(0xb8000, 0x8000);
749         if(tfb == nil)
750                 sysfatal("got nil ptr for text framebuffer");
751         for(i = 0; i < 0x8000; i += 2)
752                 PUT16(tfb, i, 0x0720);
753         if(fbsz != 0){
754                 fb = gptr(fbaddr, fbsz);
755                 if(fb == nil)
756                         sysfatal("got nil ptr for framebuffer");
757         }
758         snprint(buf, sizeof(buf), "-dx %d -dy %d", maxw+50, maxh+50);
759         newwindow(buf);
760         initdraw(nil, nil, "vmx");
761         screeninit(1);
762         flushimage(display, 1);
763         kbdlayout("/sys/lib/kbmap/us");
764         mc = initmouse(nil, screen);
765         kbdch = chancreate(sizeof(ulong), 128);
766         mousech = chancreate(sizeof(Mouse), 32);
767         proccreate(mousethread, nil, 4096);
768         proccreate(keyproc, nil, 4096);
769         proccreate(drawproc, nil, 4096);
770         if(vesamode){
771                 d = mkpcidev(allocbdf(), 0x06660666, 0x03000000, 0);
772                 mkpcibar(d, BARMEM32 | BARPREF, fbaddr, fbsz, nil, nil);
773                 vesainit();
774         }
775 }