Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libvgl / main.c
1 /*-
2  * Copyright (c) 1991-1997 Søren Schmidt
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software withough specific prior written permission
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD: src/lib/libvgl/main.c,v 1.6.2.2 2001/07/30 14:31:30 yokota Exp $
29  */
30
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <sys/signal.h>
34 #include <sys/file.h>
35 #include <sys/ioctl.h>
36 #include <sys/mman.h>
37 #include <machine/console.h>
38 #include "vgl.h"
39
40 #define min(x, y)       (((x) < (y)) ? (x) : (y))
41 #define max(x, y)       (((x) > (y)) ? (x) : (y))
42
43 VGLBitmap *VGLDisplay;
44 video_info_t VGLModeInfo;
45 video_adapter_info_t VGLAdpInfo;
46 byte *VGLBuf;
47
48 static int VGLMode;
49 static int VGLOldMode;
50 static size_t VGLBufSize;
51 static byte *VGLMem = MAP_FAILED;
52 static int VGLSwitchPending;
53 static int VGLAbortPending;
54 static int VGLOnDisplay;
55 static unsigned int VGLCurWindow;
56 static int VGLInitDone = 0;
57 static struct winsize VGLOldWSize;
58
59 void
60 VGLEnd()
61 {
62 struct vt_mode smode;
63
64   if (!VGLInitDone)
65     return;
66   VGLInitDone = 0;
67   VGLSwitchPending = 0;
68   VGLAbortPending = 0;
69
70   signal(SIGUSR1, SIG_IGN);
71
72   if (VGLMem != MAP_FAILED) {
73     VGLClear(VGLDisplay, 0);
74     munmap(VGLMem, VGLAdpInfo.va_window_size);
75   }
76
77   if (VGLOldMode >= M_VESA_BASE) {
78     /* ugly, but necessary */
79     ioctl(0, _IO('V', VGLOldMode - M_VESA_BASE), 0);
80     if (VGLOldMode == M_VESA_800x600) {
81       int size[3];
82       size[0] = VGLOldWSize.ws_col;
83       size[1] = VGLOldWSize.ws_row;
84       size[2] = 16;
85       ioctl(0, KDRASTER, size);
86     }
87   } else {
88     ioctl(0, _IO('S', VGLOldMode), 0);
89   }
90   ioctl(0, KDDISABIO, 0);
91   ioctl(0, KDSETMODE, KD_TEXT);
92   smode.mode = VT_AUTO;
93   ioctl(0, VT_SETMODE, &smode);
94   if (VGLBuf)
95     free(VGLBuf);
96   VGLBuf = NULL;
97   free(VGLDisplay);
98   VGLDisplay = NULL;
99   VGLKeyboardEnd();
100 }
101
102 static void 
103 VGLAbort()
104 {
105   VGLAbortPending = 1;
106   signal(SIGINT, SIG_IGN);
107   signal(SIGTERM, SIG_IGN);
108   signal(SIGSEGV, SIG_IGN);
109   signal(SIGBUS, SIG_IGN);
110   signal(SIGUSR2, SIG_IGN);
111 }
112
113 static void
114 VGLSwitch()
115 {
116   if (!VGLOnDisplay)
117     VGLOnDisplay = 1;
118   else
119     VGLOnDisplay = 0;
120   VGLSwitchPending = 1;
121   signal(SIGUSR1, VGLSwitch);
122 }
123
124 int
125 VGLInit(int mode)
126 {
127   struct vt_mode smode;
128   int adptype;
129
130   if (VGLInitDone)
131     return -1;
132
133   signal(SIGUSR1, VGLSwitch);
134   signal(SIGINT, VGLAbort);
135   signal(SIGTERM, VGLAbort);
136   signal(SIGSEGV, VGLAbort);
137   signal(SIGBUS, VGLAbort);
138   signal(SIGUSR2, SIG_IGN);
139
140   VGLOnDisplay = 1;
141   VGLSwitchPending = 0;
142   VGLAbortPending = 0;
143
144   if (ioctl(0, CONS_GET, &VGLOldMode) || ioctl(0, CONS_CURRENT, &adptype))
145     return -1;
146   if (IOCGROUP(mode) == 'V')    /* XXX: this is ugly */
147     VGLModeInfo.vi_mode = (mode & 0x0ff) + M_VESA_BASE;
148   else
149     VGLModeInfo.vi_mode = mode & 0x0ff;
150   if (ioctl(0, CONS_MODEINFO, &VGLModeInfo))    /* FBIO_MODEINFO */
151     return -1;
152
153   /* If current mode is VESA_800x600 then save its geometry to restore later */
154   if ((VGLOldMode >= M_VESA_BASE) && (VGLOldMode == M_VESA_800x600))
155     if (ioctl(0, TIOCGWINSZ, &VGLOldWSize))
156       return -1;
157
158   VGLDisplay = (VGLBitmap *)malloc(sizeof(VGLBitmap));
159   if (VGLDisplay == NULL)
160     return -2;
161
162   if (ioctl(0, KDENABIO, 0)) {
163     free(VGLDisplay);
164     return -3;
165   }
166
167   VGLInitDone = 1;
168
169   /*
170    * vi_mem_model specifies the memory model of the current video mode
171    * in -CURRENT.
172    */
173   switch (VGLModeInfo.vi_mem_model) {
174   case V_INFO_MM_PLANAR:
175     /* we can handle EGA/VGA planner modes only */
176     if (VGLModeInfo.vi_depth != 4 || VGLModeInfo.vi_planes != 4
177         || (adptype != KD_EGA && adptype != KD_VGA)) {
178       VGLEnd();
179       return -4;
180     }
181     VGLDisplay->Type = VIDBUF4;
182     break;
183   case V_INFO_MM_PACKED:
184     /* we can do only 256 color packed modes */
185     if (VGLModeInfo.vi_depth != 8) {
186       VGLEnd();
187       return -4;
188     }
189     VGLDisplay->Type = VIDBUF8;
190     break;
191   case V_INFO_MM_VGAX:
192     VGLDisplay->Type = VIDBUF8X;
193     break;
194   default:
195     VGLEnd();
196     return -4;
197   }
198
199   ioctl(0, VT_WAITACTIVE, 0);
200   ioctl(0, KDSETMODE, KD_GRAPHICS);
201   if (ioctl(0, mode, 0)) {
202     VGLEnd();
203     return -5;
204   }
205   if (ioctl(0, CONS_ADPINFO, &VGLAdpInfo)) {    /* FBIO_ADPINFO */
206     VGLEnd();
207     return -6;
208   }
209
210   /*
211    * Calculate the shadow screen buffer size.  In -CURRENT, va_buffer_size
212    * always holds the entire frame buffer size, wheather it's in the linear
213    * mode or windowed mode.  
214    *     VGLBufSize = VGLAdpInfo.va_buffer_size;
215    * In -STABLE, va_buffer_size holds the frame buffer size, only if
216    * the linear frame buffer mode is supported. Otherwise the field is zero.
217    * We shall calculate the minimal size in this case:
218    *     VGLAdpInfo.va_line_width*VGLModeInfo.vi_height*VGLModeInfo.vi_planes
219    * or
220    *     VGLAdpInfo.va_window_size*VGLModeInfo.vi_planes;
221    * Use whichever is larger.
222    */
223   if (VGLAdpInfo.va_buffer_size != 0)
224     VGLBufSize = VGLAdpInfo.va_buffer_size;
225   else
226     VGLBufSize = max(VGLAdpInfo.va_line_width*VGLModeInfo.vi_height,
227                      VGLAdpInfo.va_window_size)*VGLModeInfo.vi_planes;
228   VGLBuf = malloc(VGLBufSize);
229   if (VGLBuf == NULL) {
230     VGLEnd();
231     return -7;
232   }
233
234 #ifdef LIBVGL_DEBUG
235   fprintf(stderr, "VGLBufSize:0x%x\n", VGLBufSize);
236 #endif
237
238   /* see if we are in the windowed buffer mode or in the linear buffer mode */
239   if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) {
240     if (VGLDisplay->Type == VIDBUF4)
241       VGLDisplay->Type = VIDBUF4S;
242     else if (VGLDisplay->Type == VIDBUF8)
243       VGLDisplay->Type = VIDBUF8S;
244   }
245
246   VGLMode = mode;
247   VGLCurWindow = 0;
248
249   VGLDisplay->Xsize = VGLModeInfo.vi_width;
250   VGLDisplay->Ysize = VGLModeInfo.vi_height;
251   VGLDisplay->VXsize = VGLAdpInfo.va_line_width
252                            *8/(VGLModeInfo.vi_depth/VGLModeInfo.vi_planes);
253   VGLDisplay->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
254   VGLDisplay->Xorigin = 0;
255   VGLDisplay->Yorigin = 0;
256
257   VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
258                        MAP_FILE, 0, 0);
259   if (VGLMem == MAP_FAILED) {
260     VGLEnd();
261     return -7;
262   }
263   VGLDisplay->Bitmap = VGLMem;
264
265   VGLSavePalette();
266
267 #ifdef LIBVGL_DEBUG
268   fprintf(stderr, "va_line_width:%d\n", VGLAdpInfo.va_line_width);
269   fprintf(stderr, "VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
270           VGLDisplay->Xsize, VGLDisplay->Ysize, 
271           VGLDisplay->VXsize, VGLDisplay->VYsize);
272 #endif
273
274   smode.mode = VT_PROCESS;
275   smode.waitv = 0;
276   smode.relsig = SIGUSR1;
277   smode.acqsig = SIGUSR1;
278   smode.frsig  = SIGINT;        
279   if (ioctl(0, VT_SETMODE, &smode)) {
280     VGLEnd();
281     return -9;
282   }
283   VGLTextSetFontFile((byte*)0);
284   VGLClear(VGLDisplay, 0);
285   return 0;
286 }
287
288 void
289 VGLCheckSwitch()
290 {
291   if (VGLAbortPending) {
292     VGLEnd();
293     exit(0);
294   }
295   while (VGLSwitchPending) {
296     unsigned int offset;
297     unsigned int len;
298     int i;
299
300     VGLSwitchPending = 0;
301     if (VGLOnDisplay) {
302       ioctl(0, KDENABIO, 0);
303       ioctl(0, KDSETMODE, KD_GRAPHICS);
304       ioctl(0, VGLMode, 0);
305       VGLCurWindow = 0;
306       VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
307                            MAP_FILE, 0, 0);
308
309       /* XXX: what if mmap() has failed! */
310       VGLDisplay->Type = VIDBUF8;       /* XXX */
311       switch (VGLModeInfo.vi_mem_model) {
312       case V_INFO_MM_PLANAR:
313         if (VGLModeInfo.vi_depth == 4 && VGLModeInfo.vi_planes == 4) {
314           if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
315             VGLDisplay->Type = VIDBUF4S;
316           else
317             VGLDisplay->Type = VIDBUF4;
318         } else {
319           /* shouldn't be happening */
320         }
321         break;
322       case V_INFO_MM_PACKED:
323         if (VGLModeInfo.vi_depth == 8) {
324           if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
325             VGLDisplay->Type = VIDBUF8S;
326           else
327             VGLDisplay->Type = VIDBUF8;
328         } else {
329           /* shouldn't be happening */
330         }
331         break;
332       case V_INFO_MM_VGAX:
333         VGLDisplay->Type = VIDBUF8X;
334         break;
335       default:
336         /* shouldn't be happening */
337         break;
338       }
339
340       VGLDisplay->Bitmap = VGLMem;
341       VGLDisplay->Xsize = VGLModeInfo.vi_width;
342       VGLDisplay->Ysize = VGLModeInfo.vi_height;
343       VGLSetVScreenSize(VGLDisplay, VGLDisplay->VXsize, VGLDisplay->VYsize);
344       VGLPanScreen(VGLDisplay, VGLDisplay->Xorigin, VGLDisplay->Yorigin);
345       switch (VGLDisplay->Type) {
346       case VIDBUF4S:
347         outb(0x3c6, 0xff);
348         outb(0x3ce, 0x01); outb(0x3cf, 0x00);           /* set/reset enable */
349         outb(0x3ce, 0x08); outb(0x3cf, 0xff);           /* bit mask */
350         for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
351              offset += len) {
352           VGLSetSegment(offset);
353           len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
354                     VGLAdpInfo.va_window_size);
355           for (i = 0; i < VGLModeInfo.vi_planes; i++) {
356             outb(0x3c4, 0x02);
357             outb(0x3c5, 0x01<<i);
358             bcopy(&VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
359                   VGLMem, len);
360           }
361         }
362         break;
363       case VIDBUF4:
364       case VIDBUF8X:
365         outb(0x3c6, 0xff);
366         outb(0x3ce, 0x01); outb(0x3cf, 0x00);           /* set/reset enable */
367         outb(0x3ce, 0x08); outb(0x3cf, 0xff);           /* bit mask */
368         for (i = 0; i < VGLModeInfo.vi_planes; i++) {
369           outb(0x3c4, 0x02);
370           outb(0x3c5, 0x01<<i);
371           bcopy(&VGLBuf[i*VGLAdpInfo.va_window_size], VGLMem,
372                 VGLAdpInfo.va_window_size);
373         }
374         break;
375       case VIDBUF8:
376       case VIDBUF8S:
377         for (offset = 0; offset < VGLBufSize; offset += len) {
378           VGLSetSegment(offset);
379           len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
380           bcopy(&VGLBuf[offset], VGLMem, len);
381         }
382         break;
383       }
384       VGLRestorePalette();
385       ioctl(0, VT_RELDISP, VT_ACKACQ);
386     }
387     else {
388       switch (VGLDisplay->Type) {
389       case VIDBUF4S:
390         for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
391              offset += len) {
392           VGLSetSegment(offset);
393           len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
394                     VGLAdpInfo.va_window_size);
395           for (i = 0; i < VGLModeInfo.vi_planes; i++) {
396             outb(0x3ce, 0x04);
397             outb(0x3cf, i);
398             bcopy(VGLMem, &VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
399                   len);
400           }
401         }
402         break;
403       case VIDBUF4:
404       case VIDBUF8X:
405         /*
406          * NOTE: the saved buffer is NOT in the MEMBUF format which 
407          * the ordinary memory bitmap object is stored in. XXX
408          */
409         for (i = 0; i < VGLModeInfo.vi_planes; i++) {
410           outb(0x3ce, 0x04);
411           outb(0x3cf, i);
412           bcopy(VGLMem, &VGLBuf[i*VGLAdpInfo.va_window_size],
413                 VGLAdpInfo.va_window_size);
414         }
415         break;
416       case VIDBUF8:
417       case VIDBUF8S:
418         for (offset = 0; offset < VGLBufSize; offset += len) {
419           VGLSetSegment(offset);
420           len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
421           bcopy(VGLMem, &VGLBuf[offset], len);
422         }
423         break;
424       }
425       VGLMem = MAP_FAILED;
426       munmap(VGLDisplay->Bitmap, VGLAdpInfo.va_window_size);
427       ioctl(0, VGLOldMode, 0);
428       ioctl(0, KDSETMODE, KD_TEXT);
429       ioctl(0, KDDISABIO, 0);
430       ioctl(0, VT_RELDISP, VT_TRUE);
431       VGLDisplay->Bitmap = VGLBuf;
432       VGLDisplay->Type = MEMBUF;
433       VGLDisplay->Xsize = VGLDisplay->VXsize;
434       VGLDisplay->Ysize = VGLDisplay->VYsize;
435       while (!VGLOnDisplay) pause();
436     }
437   }
438 }
439
440 int
441 VGLSetSegment(unsigned int offset)
442 {
443   if (offset/VGLAdpInfo.va_window_size != VGLCurWindow) {
444     ioctl(0, CONS_SETWINORG, offset);           /* FBIO_SETWINORG */
445     VGLCurWindow = offset/VGLAdpInfo.va_window_size;
446   }
447   return (offset%VGLAdpInfo.va_window_size);
448 }
449
450 int
451 VGLSetVScreenSize(VGLBitmap *object, int VXsize, int VYsize)
452 {
453   if (VXsize < object->Xsize || VYsize < object->Ysize)
454     return -1;
455   if (object->Type == MEMBUF)
456     return -1;
457   if (ioctl(0, FBIO_SETLINEWIDTH, &VXsize))
458     return -1;
459   ioctl(0, CONS_ADPINFO, &VGLAdpInfo);  /* FBIO_ADPINFO */
460   object->VXsize = VGLAdpInfo.va_line_width
461                            *8/(VGLModeInfo.vi_depth/VGLModeInfo.vi_planes);
462   object->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
463   if (VYsize < object->VYsize)
464     object->VYsize = VYsize;
465
466 #ifdef LIBVGL_DEBUG
467   fprintf(stderr, "new size: VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
468           object->Xsize, object->Ysize, object->VXsize, object->VYsize);
469 #endif
470
471   return 0;
472 }
473
474 int
475 VGLPanScreen(VGLBitmap *object, int x, int y)
476 {
477   video_display_start_t origin;
478
479   if (x < 0 || x + object->Xsize > object->VXsize
480       || y < 0 || y + object->Ysize > object->VYsize)
481     return -1;
482   if (object->Type == MEMBUF)
483     return 0;
484   origin.x = x;
485   origin.y = y;
486   if (ioctl(0, FBIO_SETDISPSTART, &origin))
487     return -1;
488   object->Xorigin = x;
489   object->Yorigin = y;
490
491 #ifdef LIBVGL_DEBUG
492   fprintf(stderr, "new origin: (%d, %d)\n", x, y);
493 #endif
494
495   return 0;
496 }