nrelease - fix/improve livecd
[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 without 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
58 void
59 VGLEnd(void)
60 {
61 struct vt_mode smode;
62
63   if (!VGLInitDone)
64     return;
65   VGLInitDone = 0;
66   VGLSwitchPending = 0;
67   VGLAbortPending = 0;
68
69   signal(SIGUSR1, SIG_IGN);
70
71   if (VGLMem != MAP_FAILED) {
72     VGLClear(VGLDisplay, 0);
73     munmap(VGLMem, VGLAdpInfo.va_window_size);
74   }
75
76   ioctl(0, _IO('S', VGLOldMode), 0);
77   ioctl(0, KDDISABIO, 0);
78   ioctl(0, KDSETMODE, KD_TEXT);
79   smode.mode = VT_AUTO;
80   ioctl(0, VT_SETMODE, &smode);
81   if (VGLBuf)
82     free(VGLBuf);
83   VGLBuf = NULL;
84   free(VGLDisplay);
85   VGLDisplay = NULL;
86   VGLKeyboardEnd();
87 }
88
89 static void 
90 VGLAbort(int signo __unused)
91 {
92   VGLAbortPending = 1;
93   signal(SIGINT, SIG_IGN);
94   signal(SIGTERM, SIG_IGN);
95   signal(SIGSEGV, SIG_IGN);
96   signal(SIGBUS, SIG_IGN);
97   signal(SIGUSR2, SIG_IGN);
98 }
99
100 static void
101 VGLSwitch(int signo __unused)
102 {
103   if (!VGLOnDisplay)
104     VGLOnDisplay = 1;
105   else
106     VGLOnDisplay = 0;
107   VGLSwitchPending = 1;
108   signal(SIGUSR1, VGLSwitch);
109 }
110
111 int
112 VGLInit(int mode)
113 {
114   struct vt_mode smode;
115   int adptype;
116
117   if (VGLInitDone)
118     return -1;
119
120   signal(SIGUSR1, VGLSwitch);
121   signal(SIGINT, VGLAbort);
122   signal(SIGTERM, VGLAbort);
123   signal(SIGSEGV, VGLAbort);
124   signal(SIGBUS, VGLAbort);
125   signal(SIGUSR2, SIG_IGN);
126
127   VGLOnDisplay = 1;
128   VGLSwitchPending = 0;
129   VGLAbortPending = 0;
130
131   if (ioctl(0, CONS_GET, &VGLOldMode) || ioctl(0, CONS_CURRENT, &adptype))
132     return -1;
133   VGLModeInfo.vi_mode = mode;
134   if (ioctl(0, CONS_MODEINFO, &VGLModeInfo))    /* FBIO_MODEINFO */
135     return -1;
136
137   VGLDisplay = (VGLBitmap *)malloc(sizeof(VGLBitmap));
138   if (VGLDisplay == NULL)
139     return -2;
140
141   if (ioctl(0, KDENABIO, 0)) {
142     free(VGLDisplay);
143     return -3;
144   }
145
146   VGLInitDone = 1;
147
148   /*
149    * vi_mem_model specifies the memory model of the current video mode
150    * in -CURRENT.
151    */
152   switch (VGLModeInfo.vi_mem_model) {
153   case V_INFO_MM_PLANAR:
154     /* we can handle EGA/VGA planner modes only */
155     if (VGLModeInfo.vi_depth != 4 || VGLModeInfo.vi_planes != 4
156         || (adptype != KD_EGA && adptype != KD_VGA)) {
157       VGLEnd();
158       return -4;
159     }
160     VGLDisplay->Type = VIDBUF4;
161     break;
162   case V_INFO_MM_PACKED:
163     /* we can do only 256 color packed modes */
164     if (VGLModeInfo.vi_depth != 8) {
165       VGLEnd();
166       return -4;
167     }
168     VGLDisplay->Type = VIDBUF8;
169     break;
170   case V_INFO_MM_VGAX:
171     VGLDisplay->Type = VIDBUF8X;
172     break;
173   default:
174     VGLEnd();
175     return -4;
176   }
177
178   ioctl(0, VT_WAITACTIVE, 0);
179   ioctl(0, KDSETMODE, KD_GRAPHICS);
180   if (ioctl(0, CONS_SET, &mode)) {
181     VGLEnd();
182     return -5;
183   }
184   if (ioctl(0, CONS_ADPINFO, &VGLAdpInfo)) {    /* FBIO_ADPINFO */
185     VGLEnd();
186     return -6;
187   }
188
189   /*
190    * Calculate the shadow screen buffer size.  In -CURRENT, va_buffer_size
191    * always holds the entire frame buffer size, wheather it's in the linear
192    * mode or windowed mode.  
193    *     VGLBufSize = VGLAdpInfo.va_buffer_size;
194    * In -STABLE, va_buffer_size holds the frame buffer size, only if
195    * the linear frame buffer mode is supported. Otherwise the field is zero.
196    * We shall calculate the minimal size in this case:
197    *     VGLAdpInfo.va_line_width*VGLModeInfo.vi_height*VGLModeInfo.vi_planes
198    * or
199    *     VGLAdpInfo.va_window_size*VGLModeInfo.vi_planes;
200    * Use whichever is larger.
201    */
202   if (VGLAdpInfo.va_buffer_size != 0)
203     VGLBufSize = VGLAdpInfo.va_buffer_size;
204   else
205     VGLBufSize = max(VGLAdpInfo.va_line_width*VGLModeInfo.vi_height,
206                      VGLAdpInfo.va_window_size)*VGLModeInfo.vi_planes;
207   VGLBuf = malloc(VGLBufSize);
208   if (VGLBuf == NULL) {
209     VGLEnd();
210     return -7;
211   }
212
213 #ifdef LIBVGL_DEBUG
214   fprintf(stderr, "VGLBufSize:0x%x\n", VGLBufSize);
215 #endif
216
217   /* see if we are in the windowed buffer mode or in the linear buffer mode */
218   if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) {
219     if (VGLDisplay->Type == VIDBUF4)
220       VGLDisplay->Type = VIDBUF4S;
221     else if (VGLDisplay->Type == VIDBUF8)
222       VGLDisplay->Type = VIDBUF8S;
223   }
224
225   VGLMode = mode;
226   VGLCurWindow = 0;
227
228   VGLDisplay->Xsize = VGLModeInfo.vi_width;
229   VGLDisplay->Ysize = VGLModeInfo.vi_height;
230   VGLDisplay->VXsize = VGLAdpInfo.va_line_width
231                            *8/(VGLModeInfo.vi_depth/VGLModeInfo.vi_planes);
232   VGLDisplay->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
233   VGLDisplay->Xorigin = 0;
234   VGLDisplay->Yorigin = 0;
235
236   VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
237                        MAP_FILE, 0, 0);
238   if (VGLMem == MAP_FAILED) {
239     VGLEnd();
240     return -7;
241   }
242   VGLDisplay->Bitmap = VGLMem;
243
244   VGLSavePalette();
245
246 #ifdef LIBVGL_DEBUG
247   fprintf(stderr, "va_line_width:%d\n", VGLAdpInfo.va_line_width);
248   fprintf(stderr, "VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
249           VGLDisplay->Xsize, VGLDisplay->Ysize, 
250           VGLDisplay->VXsize, VGLDisplay->VYsize);
251 #endif
252
253   smode.mode = VT_PROCESS;
254   smode.waitv = 0;
255   smode.relsig = SIGUSR1;
256   smode.acqsig = SIGUSR1;
257   smode.frsig  = SIGINT;        
258   if (ioctl(0, VT_SETMODE, &smode)) {
259     VGLEnd();
260     return -9;
261   }
262   VGLTextSetFontFile(NULL);
263   VGLClear(VGLDisplay, 0);
264   return 0;
265 }
266
267 void
268 VGLCheckSwitch(void)
269 {
270   if (VGLAbortPending) {
271     VGLEnd();
272     exit(0);
273   }
274   while (VGLSwitchPending) {
275     unsigned int offset;
276     unsigned int len;
277     int i;
278
279     VGLSwitchPending = 0;
280     if (VGLOnDisplay) {
281       ioctl(0, KDENABIO, 0);
282       ioctl(0, KDSETMODE, KD_GRAPHICS);
283       ioctl(0, VGLMode, 0);
284       VGLCurWindow = 0;
285       VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
286                            MAP_FILE, 0, 0);
287
288       /* XXX: what if mmap() has failed! */
289       VGLDisplay->Type = VIDBUF8;       /* XXX */
290       switch (VGLModeInfo.vi_mem_model) {
291       case V_INFO_MM_PLANAR:
292         if (VGLModeInfo.vi_depth == 4 && VGLModeInfo.vi_planes == 4) {
293           if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
294             VGLDisplay->Type = VIDBUF4S;
295           else
296             VGLDisplay->Type = VIDBUF4;
297         } else {
298           /* shouldn't be happening */
299         }
300         break;
301       case V_INFO_MM_PACKED:
302         if (VGLModeInfo.vi_depth == 8) {
303           if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
304             VGLDisplay->Type = VIDBUF8S;
305           else
306             VGLDisplay->Type = VIDBUF8;
307         } else {
308           /* shouldn't be happening */
309         }
310         break;
311       case V_INFO_MM_VGAX:
312         VGLDisplay->Type = VIDBUF8X;
313         break;
314       default:
315         /* shouldn't be happening */
316         break;
317       }
318
319       VGLDisplay->Bitmap = VGLMem;
320       VGLDisplay->Xsize = VGLModeInfo.vi_width;
321       VGLDisplay->Ysize = VGLModeInfo.vi_height;
322       VGLSetVScreenSize(VGLDisplay, VGLDisplay->VXsize, VGLDisplay->VYsize);
323       VGLPanScreen(VGLDisplay, VGLDisplay->Xorigin, VGLDisplay->Yorigin);
324       switch (VGLDisplay->Type) {
325       case VIDBUF4S:
326         outb(0x3c6, 0xff);
327         outb(0x3ce, 0x01); outb(0x3cf, 0x00);           /* set/reset enable */
328         outb(0x3ce, 0x08); outb(0x3cf, 0xff);           /* bit mask */
329         for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
330              offset += len) {
331           VGLSetSegment(offset);
332           len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
333                     VGLAdpInfo.va_window_size);
334           for (i = 0; i < VGLModeInfo.vi_planes; i++) {
335             outb(0x3c4, 0x02);
336             outb(0x3c5, 0x01<<i);
337             bcopy(&VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
338                   VGLMem, len);
339           }
340         }
341         break;
342       case VIDBUF4:
343       case VIDBUF8X:
344         outb(0x3c6, 0xff);
345         outb(0x3ce, 0x01); outb(0x3cf, 0x00);           /* set/reset enable */
346         outb(0x3ce, 0x08); outb(0x3cf, 0xff);           /* bit mask */
347         for (i = 0; i < VGLModeInfo.vi_planes; i++) {
348           outb(0x3c4, 0x02);
349           outb(0x3c5, 0x01<<i);
350           bcopy(&VGLBuf[i*VGLAdpInfo.va_window_size], VGLMem,
351                 VGLAdpInfo.va_window_size);
352         }
353         break;
354       case VIDBUF8:
355       case VIDBUF8S:
356         for (offset = 0; offset < VGLBufSize; offset += len) {
357           VGLSetSegment(offset);
358           len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
359           bcopy(&VGLBuf[offset], VGLMem, len);
360         }
361         break;
362       }
363       VGLRestorePalette();
364       ioctl(0, VT_RELDISP, VT_ACKACQ);
365     }
366     else {
367       switch (VGLDisplay->Type) {
368       case VIDBUF4S:
369         for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
370              offset += len) {
371           VGLSetSegment(offset);
372           len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
373                     VGLAdpInfo.va_window_size);
374           for (i = 0; i < VGLModeInfo.vi_planes; i++) {
375             outb(0x3ce, 0x04);
376             outb(0x3cf, i);
377             bcopy(VGLMem, &VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
378                   len);
379           }
380         }
381         break;
382       case VIDBUF4:
383       case VIDBUF8X:
384         /*
385          * NOTE: the saved buffer is NOT in the MEMBUF format which 
386          * the ordinary memory bitmap object is stored in. XXX
387          */
388         for (i = 0; i < VGLModeInfo.vi_planes; i++) {
389           outb(0x3ce, 0x04);
390           outb(0x3cf, i);
391           bcopy(VGLMem, &VGLBuf[i*VGLAdpInfo.va_window_size],
392                 VGLAdpInfo.va_window_size);
393         }
394         break;
395       case VIDBUF8:
396       case VIDBUF8S:
397         for (offset = 0; offset < VGLBufSize; offset += len) {
398           VGLSetSegment(offset);
399           len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
400           bcopy(VGLMem, &VGLBuf[offset], len);
401         }
402         break;
403       }
404       VGLMem = MAP_FAILED;
405       munmap(VGLDisplay->Bitmap, VGLAdpInfo.va_window_size);
406       ioctl(0, VGLOldMode, 0);
407       ioctl(0, KDSETMODE, KD_TEXT);
408       ioctl(0, KDDISABIO, 0);
409       ioctl(0, VT_RELDISP, VT_TRUE);
410       VGLDisplay->Bitmap = VGLBuf;
411       VGLDisplay->Type = MEMBUF;
412       VGLDisplay->Xsize = VGLDisplay->VXsize;
413       VGLDisplay->Ysize = VGLDisplay->VYsize;
414       while (!VGLOnDisplay) pause();
415     }
416   }
417 }
418
419 int
420 VGLSetSegment(unsigned int offset)
421 {
422   if (offset/VGLAdpInfo.va_window_size != VGLCurWindow) {
423     ioctl(0, CONS_SETWINORG, offset);           /* FBIO_SETWINORG */
424     VGLCurWindow = offset/VGLAdpInfo.va_window_size;
425   }
426   return (offset%VGLAdpInfo.va_window_size);
427 }
428
429 int
430 VGLSetVScreenSize(VGLBitmap *object, int VXsize, int VYsize)
431 {
432   if (VXsize < object->Xsize || VYsize < object->Ysize)
433     return -1;
434   if (object->Type == MEMBUF)
435     return -1;
436   if (ioctl(0, FBIO_SETLINEWIDTH, &VXsize))
437     return -1;
438   ioctl(0, CONS_ADPINFO, &VGLAdpInfo);  /* FBIO_ADPINFO */
439   object->VXsize = VGLAdpInfo.va_line_width
440                            *8/(VGLModeInfo.vi_depth/VGLModeInfo.vi_planes);
441   object->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
442   if (VYsize < object->VYsize)
443     object->VYsize = VYsize;
444
445 #ifdef LIBVGL_DEBUG
446   fprintf(stderr, "new size: VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
447           object->Xsize, object->Ysize, object->VXsize, object->VYsize);
448 #endif
449
450   return 0;
451 }
452
453 int
454 VGLPanScreen(VGLBitmap *object, int x, int y)
455 {
456   video_display_start_t origin;
457
458   if (x < 0 || x + object->Xsize > object->VXsize
459       || y < 0 || y + object->Ysize > object->VYsize)
460     return -1;
461   if (object->Type == MEMBUF)
462     return 0;
463   origin.x = x;
464   origin.y = y;
465   if (ioctl(0, FBIO_SETDISPSTART, &origin))
466     return -1;
467   object->Xorigin = x;
468   object->Yorigin = y;
469
470 #ifdef LIBVGL_DEBUG
471   fprintf(stderr, "new origin: (%d, %d)\n", x, y);
472 #endif
473
474   return 0;
475 }