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