]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vmx/vga.c
b56b9687713c24fb6ab157d7c52dfcb0e30c5859
[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 "dat.h"
10 #include "fns.h"
11
12 static uchar *fb;
13 uintptr fbsz;
14 uintptr fbaddr;
15 int textmode;
16 static ulong screenchan;
17
18 static int picw, pich, hbytes;
19 static Image *img, *bg;
20 static Mousectl *mc;
21 static Rectangle picr;
22 Channel *kbdch, *mousech;
23 u8int mousegrab;
24 static uchar *sfb;
25
26 typedef struct VGA VGA;
27 struct VGA {
28         u8int miscout;
29         u8int cidx;
30         u8int aidx; /* bit 7: access flipflop */
31         u16int rdidx, wdidx; /* bit 0-1: color */
32         u8int attr[32];
33         u32int pal[256];
34         Image *col[256];
35         Image *acol[16];
36         u8int crtc[0x18];
37 } vga = {
38         .miscout 1,
39         .attr { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
40         .crtc { [11] 15 },
41         .pal {
42                 0x000000ff, 0x0000a8ff, 0x00a800ff, 0x00a8a8ff, 0xa80000ff, 0xa800a8ff, 0xa85400ff, 0xa8a8a8ff,
43                 0x545454ff, 0x5454fcff, 0x54fc54ff, 0x54fcfcff, 0xfc5454ff, 0xfc54fcff, 0xfcfc54ff, 0xfcfcfcff,
44                 0x000000ff, 0x141414ff, 0x202020ff, 0x2c2c2cff, 0x383838ff, 0x444444ff, 0x505050ff, 0x606060ff,
45                 0x707070ff, 0x808080ff, 0x909090ff, 0xa0a0a0ff, 0xb4b4b4ff, 0xc8c8c8ff, 0xe0e0e0ff, 0xfcfcfcff,
46                 0x0000fcff, 0x4000fcff, 0x7c00fcff, 0xbc00fcff, 0xfc00fcff, 0xfc00bcff, 0xfc007cff, 0xfc0040ff,
47                 0xfc0000ff, 0xfc4000ff, 0xfc7c00ff, 0xfcbc00ff, 0xfcfc00ff, 0xbcfc00ff, 0x7cfc00ff, 0x40fc00ff,
48                 0x00fc00ff, 0x00fc40ff, 0x00fc7cff, 0x00fcbcff, 0x00fcfcff, 0x00bcfcff, 0x007cfcff, 0x0040fcff,
49                 0x7c7cfcff, 0x9c7cfcff, 0xbc7cfcff, 0xdc7cfcff, 0xfc7cfcff, 0xfc7cdcff, 0xfc7cbcff, 0xfc7c9cff,
50                 0xfc7c7cff, 0xfc9c7cff, 0xfcbc7cff, 0xfcdc7cff, 0xfcfc7cff, 0xdcfc7cff, 0xbcfc7cff, 0x9cfc7cff,
51                 0x7cfc7cff, 0x7cfc9cff, 0x7cfcbcff, 0x7cfcdcff, 0x7cfcfcff, 0x7cdcfcff, 0x7cbcfcff, 0x7c9cfcff,
52                 0xb4b4fcff, 0xc4b4fcff, 0xd8b4fcff, 0xe8b4fcff, 0xfcb4fcff, 0xfcb4e8ff, 0xfcb4d8ff, 0xfcb4c4ff,
53                 0xfcb4b4ff, 0xfcc4b4ff, 0xfcd8b4ff, 0xfce8b4ff, 0xfcfcb4ff, 0xe8fcb4ff, 0xd8fcb4ff, 0xc4fcb4ff,
54                 0xb4fcb4ff, 0xb4fcc4ff, 0xb4fcd8ff, 0xb4fce8ff, 0xb4fcfcff, 0xb4e8fcff, 0xb4d8fcff, 0xb4c4fcff,
55                 0x000070ff, 0x1c0070ff, 0x380070ff, 0x540070ff, 0x700070ff, 0x700054ff, 0x700038ff, 0x70001cff,
56                 0x700000ff, 0x701c00ff, 0x703800ff, 0x705400ff, 0x707000ff, 0x547000ff, 0x387000ff, 0x1c7000ff,
57                 0x007000ff, 0x00701cff, 0x007038ff, 0x007054ff, 0x007070ff, 0x005470ff, 0x003870ff, 0x001c70ff,
58                 0x383870ff, 0x443870ff, 0x543870ff, 0x603870ff, 0x703870ff, 0x703860ff, 0x703854ff, 0x703844ff,
59                 0x703838ff, 0x704438ff, 0x705438ff, 0x706038ff, 0x707038ff, 0x607038ff, 0x547038ff, 0x447038ff,
60                 0x387038ff, 0x387044ff, 0x387054ff, 0x387060ff, 0x387070ff, 0x386070ff, 0x385470ff, 0x384470ff,
61                 0x505070ff, 0x585070ff, 0x605070ff, 0x685070ff, 0x705070ff, 0x705068ff, 0x705060ff, 0x705058ff,
62                 0x705050ff, 0x705850ff, 0x706050ff, 0x706850ff, 0x707050ff, 0x687050ff, 0x607050ff, 0x587050ff,
63                 0x507050ff, 0x507058ff, 0x507060ff, 0x507068ff, 0x507070ff, 0x506870ff, 0x506070ff, 0x505870ff,
64                 0x000040ff, 0x100040ff, 0x200040ff, 0x300040ff, 0x400040ff, 0x400030ff, 0x400020ff, 0x400010ff,
65                 0x400000ff, 0x401000ff, 0x402000ff, 0x403000ff, 0x404000ff, 0x304000ff, 0x204000ff, 0x104000ff,
66                 0x004000ff, 0x004010ff, 0x004020ff, 0x004030ff, 0x004040ff, 0x003040ff, 0x002040ff, 0x001040ff,
67                 0x202040ff, 0x282040ff, 0x302040ff, 0x382040ff, 0x402040ff, 0x402038ff, 0x402030ff, 0x402028ff,
68                 0x402020ff, 0x402820ff, 0x403020ff, 0x403820ff, 0x404020ff, 0x384020ff, 0x304020ff, 0x284020ff,
69                 0x204020ff, 0x204028ff, 0x204030ff, 0x204038ff, 0x204040ff, 0x203840ff, 0x203040ff, 0x202840ff,
70                 0x2c2c40ff, 0x302c40ff, 0x342c40ff, 0x3c2c40ff, 0x402c40ff, 0x402c3cff, 0x402c34ff, 0x402c30ff,
71                 0x402c2cff, 0x40302cff, 0x40342cff, 0x403c2cff, 0x40402cff, 0x3c402cff, 0x34402cff, 0x30402cff,
72                 0x2c402cff, 0x2c4030ff, 0x2c4034ff, 0x2c403cff, 0x2c4040ff, 0x2c3c40ff, 0x2c3440ff, 0x2c3040ff,
73                 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff,
74         },
75 };
76
77 static void
78 newpal(int l, int n, int dofree)
79 {
80         int x;
81
82         for(; n-- > 0; l++){
83                 if(dofree)
84                         freeimage(vga.col[l]);
85                 vga.col[l] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, vga.pal[l]);
86         }
87         for(l = 0; l < 16; l++){
88                 x = vga.attr[0x14] << 4 & 0xc0 | vga.attr[l] & 0x3f;
89                 if((vga.attr[0x10] & 0x80) != 0)
90                         x = x & 0xcf | vga.attr[0x14] << 4 & 0x30;
91                 vga.acol[l] = vga.col[x];
92         }
93 }
94
95 static void
96 screeninit(void)
97 {
98         Point p;
99
100         p = divpt(addpt(screen->r.min, screen->r.max), 2);
101         picr = (Rectangle){subpt(p, Pt(picw/2, pich/2)), addpt(p, Pt((picw+1)/2, (pich+1)/2))};
102         bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
103         img = allocimage(display, Rect(0, 0, picw, pich), screenchan == 0 ? screen->chan : screenchan, 0, 0);
104         draw(screen, screen->r, bg, nil, ZP);
105         newpal(0, 256, 0);
106 }
107
108 u32int
109 vgaio(int isin, u16int port, u32int val, int sz, void *)
110 {
111         u32int m;
112
113         iowhine(isin, port, val, sz, "vga");
114         if(sz != 1) vmdebug("vga: non-byte access to port %#ux, sz=%d", port, sz);
115         val = (u8int) val;
116         switch(isin << 16 | port){
117         case 0x3c0:
118                 if((vga.aidx & 0x80) != 0){
119                         vmdebug("vga: attribute write %#.2x = %#.2x", vga.aidx & 0x1f, val);
120                         vga.attr[vga.aidx & 0x1f] = val;
121                         newpal(0, 0, 0);
122                 }else
123                         vga.aidx = val & 0x3f;
124                 vga.aidx ^= 0x80;
125                 return 0;
126         case 0x3c2: vga.miscout = val; return 0;
127         case 0x3c7: vga.rdidx = val << 2; return 0;
128         case 0x3c8: vga.wdidx = val << 2; return 0;
129         case 0x3c9:
130                 vga.pal[vga.wdidx >> 2] = vga.pal[vga.wdidx >> 2] & ~(0xff << (~vga.wdidx << 3 & 24)) | val << 2 + (~vga.wdidx << 3 & 24);
131                 newpal(vga.wdidx >> 2, 1, 1);
132                 vga.wdidx = vga.wdidx + 1 + (vga.wdidx >> 1 & 1) & 0x3ff;
133                 return 0;
134         case 0x3d4: vga.cidx = val; return 0;
135         case 0x3d5:
136                 switch(vga.cidx){
137                 case 10: case 11: case 12: case 13: case 14: case 15:
138                         vga.crtc[vga.cidx] = val;
139                         return 0;
140                 default:
141                         vmerror("write to unknown VGA register, 3d5/%#ux (val=%#ux)", vga.cidx, val);
142                 }
143                 return 0;
144         case 0x103c0: return vga.aidx & 0x3f;
145         case 0x103c1: return vga.attr[vga.aidx & 0x1f];
146         case 0x103c7: return vga.rdidx >> 2;
147         case 0x103c8: return vga.wdidx >> 2;
148         case 0x103c9:
149                 m = vga.pal[vga.rdidx >> 2] >> (~vga.rdidx << 3 & 24) + 2;
150                 vga.rdidx = vga.rdidx + 1 + (vga.rdidx >> 1 & 1) & 0x3ff;
151                 return m;
152         case 0x103cc: return vga.miscout;
153         case 0x103d4: return vga.cidx;
154         case 0x103d5:
155                 switch(vga.cidx){
156                 case 10: case 11: case 12: case 13: case 14: case 15:
157                         return vga.crtc[vga.cidx];
158                 default:
159                         vmerror("read from unknown VGA register, 3d5/%#ux", vga.cidx);
160                         return 0;
161                 }
162         case 0x103da:
163                 vga.aidx &= ~0x7f;
164                 return 0;               
165         }
166         return iowhine(isin, port, val, sz, "vga");
167 }
168
169 typedef struct Key Key;
170 struct Key {
171         Rune r;
172         int code;
173         Key *next;
174 };
175 Key *kbdmap[128];
176
177 static void
178 defkey(Rune r, int code)
179 {
180         Key *k, **kp;
181
182         for(kp = &kbdmap[r % nelem(kbdmap)]; *kp != nil; kp = &(*kp)->next)
183                 if((*kp)->r == r)
184                         return;
185         k = emalloc(sizeof(Key));
186         k->r = r;
187         k->code = code;
188         *kp = k;
189 }
190
191 void
192 kbdlayout(char *fn)
193 {
194         Biobuf *bp;
195         char *s, *p, *f[10];
196         int nf, x, y;
197         Rune z;
198         
199         defkey(Kshift, 0x2a);
200         defkey(Kctl, 0x1d);
201         defkey(Kalt, 0x38);
202         defkey(Kctl, 0x11d);
203         defkey(Kprint, 0x137);
204         defkey(Kaltgr, 0x138);
205         defkey(Kbreak, 0x146);
206         defkey(Khome, 0x147);
207         defkey(Kup, 0x148);
208         defkey(Kpgup, 0x149);
209         defkey(Kleft, 0x14b);
210         defkey(Kright, 0x14d);
211         defkey(Kend, 0x14f);
212         defkey(Kdown, 0x150);
213         defkey(Kpgdown, 0x151);
214         defkey(Kins, 0x152);
215         defkey(Kdel, 0x153);
216         defkey(Kup, 0x179);
217
218         bp = Bopen(fn, OREAD);
219         if(bp == nil){
220                 vmerror("kbdlayout: %r");
221                 return;
222         }
223         for(;; free(s)){
224                 s = Brdstr(bp, '\n', 1);
225                 if(s == nil) break;
226                 nf = getfields(s, f, nelem(f), 1, " \t");
227                 if(nf < 3) continue;
228                 x = strtol(f[0], &p, 0);
229                 if(*p != 0) continue;
230                 y = strtol(f[1], &p, 0);
231                 if(*p != 0) continue;
232                 if(*f[2] == '\'' || *f[2] == '^'){
233                         chartorune(&z, f[2]+1);
234                         if(*f[2] == '^') z -= '@';
235                 }else{
236                         z = strtol(f[2], &p, 0);
237                         if(*p != 0) continue;
238                 }
239                 
240                 if(x != 0 || z == 0) continue;
241                 defkey(z, y);
242         }
243         Bterm(bp);
244 }
245
246 void
247 keyproc(void *)
248 {
249         int fd, n;
250         static char buf[256];
251         static uvlong kdown[8], nkdown[8];
252         uvlong set, rls;
253         int i, j;
254         char *s;
255         Rune r;
256         Key *k;
257
258         threadsetname("keyproc");
259         fd = open("/dev/kbd", OREAD);
260         if(fd < 0)
261                 sysfatal("open: %r");
262         for(;;){
263                 if(buf[0] != 0){
264                         n = strlen(buf)+1;
265                         memmove(buf, buf+n, sizeof(buf)-n);
266                 }
267                 if(buf[0] == 0){
268                         n = read(fd, buf, sizeof(buf)-1);
269                         if(n <= 0)
270                                 sysfatal("read /dev/kbd: %r");
271                         buf[n-1] = 0;
272                         buf[n] = 0;
273                 }
274                 if(buf[0] != 'k' && buf[0] != 'K')
275                         continue;
276                 s = buf + 1;
277                 memset(nkdown, 0, sizeof(nkdown));
278                 while(*s != 0){
279                         s += chartorune(&r, s);
280                         for(k = kbdmap[r % nelem(kbdmap)]; k != nil; k = k->next)
281                                 if(k->r == r){
282                                         nkdown[k->code >> 6] |= 1ULL<<(k->code&63);
283                                         break;
284                                 }
285                         if(k == nil) vmerror("unknown key %d", r);
286                 }
287                 if(mousegrab && (nkdown[0]>>29 & 1) != 0 && (nkdown[0]>>56 & 1) != 0){
288                         mousegrab = 0;
289                         setcursor(mc, nil);
290                 }
291                 for(i = 0; i < 8; i++){
292                         if(nkdown[i] == kdown[i]) continue;
293                         set = nkdown[i] & ~kdown[i];
294                         rls = ~nkdown[i] & kdown[i];
295                         for(j = 0; j < 64; j++, set>>=1, rls >>= 1)
296                                 if(((set|rls) & 1) != 0){
297                                         if(i >= 4)
298                                                 sendul(kbdch, 0xe0);
299                                         sendul(kbdch, j | i<<6&0xff | ((rls&1) != 0 ? 0x80 : 0));
300                                         sendnotif(i8042kick, nil);
301                                 }
302                         kdown[i] = nkdown[i];
303                 }
304         }
305 }
306
307 void
308 mousethread(void *)
309 {
310         Mouse m;
311         static Mouse mm, om;
312         int gotm;
313         Point mid;
314         Rectangle grabout;
315         int clicked;
316         static Cursor blank;
317         
318         gotm = 0;
319         clicked = 0;
320         for(;;){
321                 Alt a[] = {
322                         {mc->c, &m, CHANRCV},
323                         {mousech, &mm, gotm ? CHANSND : CHANNOP},
324                         {nil, nil, CHANEND},
325                 };
326                 
327                 switch(alt(a)){
328                 case 0:
329                         mid = divpt(addpt(picr.max, picr.min), 2);
330                         grabout = insetrect(Rpt(mid, mid), -50);
331                         if(!ptinrect(m.xy, picr)){
332                                 clicked = 0;
333                                 break;
334                         }
335                         if(!mousegrab){
336                                 if(clicked && (m.buttons & 1) == 0 && !textmode){
337                                         mousegrab = 1;
338                                         setcursor(mc, &blank);
339                                 }
340                                 clicked = m.buttons & 1;
341                                 break;
342                         }
343                         gotm = 1;
344                         if(!ptinrect(m.xy, grabout)){
345                                 moveto(mc, mid);
346                                 m.xy = mid;
347                                 om.xy = mid;
348                         }
349                         mm.xy = addpt(mm.xy, subpt(m.xy, om.xy));
350                         om = m;
351                         mm.buttons = m.buttons;
352                         break;
353                 case 1:
354                         sendnotif(i8042kick, nil);
355                         mm.xy = Pt(0,0);
356                         gotm = 0;
357                         break;
358                 }
359         }
360 }
361
362 static Rune cp437[256] = {
363         0x0020, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,
364         0x25ba, 0x25c4, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc, 
365         0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 
366         0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 
367         0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 
368         0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 
369         0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 
370         0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, 
371         0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 
372         0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, 
373         0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 
374         0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 
375         0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 
376         0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 
377         0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, 
378         0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0, 
379 };
380
381 static void
382 drawtext(void)
383 {
384         Rune buf[80];
385         uchar *p, attr;
386         int y, x, x1;
387         Rectangle r;
388         u16int cp;
389         static uchar rbuf[80*25*2];
390         u16int sa;
391         
392         sa = vga.crtc[12] << 8 | vga.crtc[13];
393         if(sa + 80*25 >= 0x10000){
394                 memset(rbuf, 0, sizeof(rbuf));
395                 memmove(rbuf, fb + sa * 2, 0x10000 - 80*25 - sa);
396                 p = rbuf;
397         }else
398                 p = fb + sa * 2;
399         for(y = 0; y < 25; y++){
400                 for(x = 0; x < 80; x++)
401                         buf[x] = cp437[p[2*x]];
402                 for(x = 0; x < 80; x = x1){
403                         attr = p[2*x+1];
404                         for(x1 = x; x1 < 80 && p[2*x1+1] == attr; x1++)
405                                 ;
406                         r = Rect(x * 8, y * 16, x1 * 8, (y + 1) * 16);
407                         draw(img, r, vga.acol[attr >> 4], nil, ZP);
408                         runestringn(img, r.min, vga.acol[attr & 0xf], ZP, display->defaultfont, buf + x, x1 - x);
409                 }
410                 p += 160;
411         }
412         cp = (vga.crtc[14] << 8 | vga.crtc[15]);
413         if(cp >= sa && cp < sa + 80*25 && (vga.crtc[10] & 0x20) == 0){
414                 buf[0] = cp437[fb[cp*2]];
415                 attr = fb[cp*2+1];
416                 r.min = Pt((cp - sa) % 80 * 8, (cp - sa) / 80 * 16);
417                 r.max = Pt(r.min.x + 8, r.min.y + (vga.crtc[11] & 0x1f) + 1);
418                 r.min.y += vga.crtc[10] & 0x1f;
419                 draw(img, r, vga.acol[attr & 0xf], nil, ZP);
420         }
421         draw(screen, picr, img, nil, ZP);
422         flushimage(display, 1); 
423 }
424
425 static void
426 drawfb(void)
427 {
428         u32int *p, *q;
429         Rectangle upd;
430         int xb, y;
431
432         p = (u32int *) fb;
433         q = (u32int *) sfb;
434         upd.min.y = upd.max.y = -1;
435         xb = 0;
436         y = 0;
437         while(p < (u32int*)(fb + fbsz)){
438                 if(*p != *q){
439                         if(upd.min.y < 0) upd.min.y = y;
440                         upd.max.y = y + 1 + (xb + 4 > hbytes);
441                         *q = *p;
442                 }
443                 p++;
444                 q++;
445                 xb += 4;
446                 if(xb >= hbytes){
447                         xb -= hbytes;
448                         y++;
449                 }
450         }
451         if(upd.min.y == upd.max.y) return;
452         upd.min.x = 0;
453         upd.max.x = picw;
454         if(screenchan != screen->chan){
455                 loadimage(img, upd, sfb + upd.min.y * hbytes, (upd.max.y - upd.min.y) * hbytes);
456                 draw(screen, rectaddpt(upd, picr.min), img, nil, upd.min);
457         }else
458                 loadimage(screen, rectaddpt(upd, picr.min), sfb + upd.min.y * hbytes, (upd.max.y - upd.min.y) * hbytes);
459         flushimage(display, 1);
460 }
461
462 void
463 drawproc(void *)
464 {
465         ulong ul;
466
467         threadsetname("draw");
468         sfb = emalloc(fbsz);
469         for(;; sleep(20)){
470                 while(nbrecv(mc->resizec, &ul) > 0){
471                         if(getwindow(display, Refnone) < 0)
472                                 sysfatal("resize failed: %r");
473                         screeninit();
474                 }
475                 if(textmode)
476                         drawtext();
477                 else
478                         drawfb();
479         }
480 }
481
482 void
483 vgafbparse(char *fbstring)
484 {
485         char buf[512];
486         char *p, *q;
487         uvlong addr;
488
489         if(picw != 0) sysfatal("vga specified twice");
490         if(strcmp(fbstring, "text") == 0){
491                 picw = 640;
492                 pich = 400;
493                 fbsz = 80*25*2;
494                 fbaddr = 0xb8000;
495                 textmode++;
496                 screenchan = 0;
497         }else{
498                 strecpy(buf, buf + nelem(buf), fbstring);
499                 picw = strtol(buf, &p, 10);
500                 if(*p != 'x')
501                 nope:
502                         sysfatal("vgafbparse: invalid framebuffer specifier: %#q (should be WxHxCHAN@ADDR or 'text')", fbstring);
503                 pich = strtol(p+1, &p, 10);
504                 if(*p != 'x') goto nope;
505                 q = strchr(p+1, '@');
506                 if(q == nil) goto nope;
507                 *q = 0;
508                 screenchan = strtochan(p+1);
509                 if(screenchan == 0) goto nope;
510                 p = q + 1;
511                 if(*p == 0) goto nope;
512                 addr = strtoull(p, &p, 0);
513                 fbaddr = addr;
514                 if(fbaddr != addr) goto nope;
515                 if(*p != 0) goto nope;
516                 hbytes = chantodepth(screenchan) * picw + 7 >> 3;
517                 fbsz = hbytes * pich;
518         }
519 }
520
521 void
522 vgainit(void)
523 {
524         char buf[512];
525         int i;
526
527         if(picw == 0) return;
528         fb = gptr(fbaddr, fbsz);
529         if(fb == nil)
530                 sysfatal("got nil ptr for framebuffer");
531         if(textmode)
532                 for(i = 0; i < 0x8000; i += 2)
533                         PUT16(fb, i, 0x0700);
534         snprint(buf, sizeof(buf), "-dx %d -dy %d", picw+50, pich+50);
535         newwindow(buf);
536         initdraw(nil, nil, "vmx");
537         screeninit();
538         flushimage(display, 1);
539         kbdlayout("/sys/lib/kbmap/us");
540         mc = initmouse(nil, screen);
541         kbdch = chancreate(sizeof(ulong), 128);
542         mousech = chancreate(sizeof(Mouse), 32);
543         proccreate(mousethread, nil, 4096);
544         proccreate(keyproc, nil, 4096);
545         proccreate(drawproc, nil, 4096);
546 }