Merge from vendor branch GDB:
[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.3 2007/08/19 11:39:11 swildner 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   VGLModeInfo.vi_mode = mode;
148   if (ioctl(0, CONS_MODEINFO, &VGLModeInfo))    /* FBIO_MODEINFO */
149     return -1;
150
151   /* If current mode is VESA_800x600 then save its geometry to restore later */
152   if ((VGLOldMode >= M_VESA_BASE) && (VGLOldMode == M_VESA_800x600))
153     if (ioctl(0, TIOCGWINSZ, &VGLOldWSize))
154       return -1;
155
156   VGLDisplay = (VGLBitmap *)malloc(sizeof(VGLBitmap));
157   if (VGLDisplay == NULL)
158     return -2;
159
160   if (ioctl(0, KDENABIO, 0)) {
161     free(VGLDisplay);
162     return -3;
163   }
164
165   VGLInitDone = 1;
166
167   /*
168    * vi_mem_model specifies the memory model of the current video mode
169    * in -CURRENT.
170    */
171   switch (VGLModeInfo.vi_mem_model) {
172   case V_INFO_MM_PLANAR:
173     /* we can handle EGA/VGA planner modes only */
174     if (VGLModeInfo.vi_depth != 4 || VGLModeInfo.vi_planes != 4
175         || (adptype != KD_EGA && adptype != KD_VGA)) {
176       VGLEnd();
177       return -4;
178     }
179     VGLDisplay->Type = VIDBUF4;
180     break;
181   case V_INFO_MM_PACKED:
182     /* we can do only 256 color packed modes */
183     if (VGLModeInfo.vi_depth != 8) {
184       VGLEnd();
185       return -4;
186     }
187     VGLDisplay->Type = VIDBUF8;
188     break;
189   case V_INFO_MM_VGAX:
190     VGLDisplay->Type = VIDBUF8X;
191     break;
192   default:
193     VGLEnd();
194     return -4;
195   }
196
197   ioctl(0, VT_WAITACTIVE, 0);
198   ioctl(0, KDSETMODE, KD_GRAPHICS);
199   if (ioctl(0, CONS_SET, &mode)) {
200     VGLEnd();
201     return -5;
202   }
203   if (ioctl(0, CONS_ADPINFO, &VGLAdpInfo)) {    /* FBIO_ADPINFO */
204     VGLEnd();
205     return -6;
206   }
207
208   /*
209    * Calculate the shadow screen buffer size.  In -CURRENT, va_buffer_size
210    * always holds the entire frame buffer size, wheather it's in the linear
211    * mode or windowed mode.  
212    *     VGLBufSize = VGLAdpInfo.va_buffer_size;
213    * In -STABLE, va_buffer_size holds the frame buffer size, only if
214    * the linear frame buffer mode is supported. Otherwise the field is zero.
215    * We shall calculate the minimal size in this case:
216    *     VGLAdpInfo.va_line_width*VGLModeInfo.vi_height*VGLModeInfo.vi_planes
217    * or
218    *     VGLAdpInfo.va_window_size*VGLModeInfo.vi_planes;
219    * Use whichever is larger.
220    */
221   if (VGLAdpInfo.va_buffer_size != 0)
222     VGLBufSize = VGLAdpInfo.va_buffer_size;
223   else
224     VGLBufSize = max(VGLAdpInfo.va_line_width*VGLModeInfo.vi_height,
225                      VGLAdpInfo.va_window_size)*VGLModeInfo.vi_planes;
226   VGLBuf = malloc(VGLBufSize);
227   if (VGLBuf == NULL) {
228     VGLEnd();
229     return -7;
230   }
231
232 #ifdef LIBVGL_DEBUG
233   fprintf(stderr, "VGLBufSize:0x%x\n", VGLBufSize);
234 #endif
235
236   /* see if we are in the windowed buffer mode or in the linear buffer mode */
237   if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) {
238     if (VGLDisplay->Type == VIDBUF4)
239       VGLDisplay->Type = VIDBUF4S;
240     else if (VGLDisplay->Type == VIDBUF8)
241       VGLDisplay->Type = VIDBUF8S;
242   }
243
244   VGLMode = mode;
245   VGLCurWindow = 0;
246
247   VGLDisplay->Xsize = VGLModeInfo.vi_width;
248   VGLDisplay->Ysize = VGLModeInfo.vi_height;
249   VGLDisplay->VXsize = VGLAdpInfo.va_line_width
250                            *8/(VGLModeInfo.vi_depth/VGLModeInfo.vi_planes);
251   VGLDisplay->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
252   VGLDisplay->Xorigin = 0;
253   VGLDisplay->Yorigin = 0;
254
255   VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
256                        MAP_FILE, 0, 0);
257   if (VGLMem == MAP_FAILED) {
258     VGLEnd();
259     return -7;
260   }
261   VGLDisplay->Bitmap = VGLMem;
262
263   VGLSavePalette();
264
265 #ifdef LIBVGL_DEBUG
266   fprintf(stderr, "va_line_width:%d\n", VGLAdpInfo.va_line_width);
267   fprintf(stderr, "VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
268           VGLDisplay->Xsize, VGLDisplay->Ysize, 
269           VGLDisplay->VXsize, VGLDisplay->VYsize);
270 #endif
271
272   smode.mode = VT_PROCESS;
273   smode.waitv = 0;
274   smode.relsig = SIGUSR1;
275   smode.acqsig = SIGUSR1;
276   smode.frsig  = SIGINT;        
277   if (ioctl(0, VT_SETMODE, &smode)) {
278     VGLEnd();
279     return -9;
280   }
281   VGLTextSetFontFile((byte*)0);
282   VGLClear(VGLDisplay, 0);
283   return 0;
284 }
285
286 void
287 VGLCheckSwitch()
288 {
289   if (VGLAbortPending) {
290     VGLEnd();
291     exit(0);
292   }
293   while (VGLSwitchPending) {
294     unsigned int offset;
295     unsigned int len;
296     int i;
297
298     VGLSwitchPending = 0;
299     if (VGLOnDisplay) {
300       ioctl(0, KDENABIO, 0);
301       ioctl(0, KDSETMODE, KD_GRAPHICS);
302       ioctl(0, VGLMode, 0);
303       VGLCurWindow = 0;
304       VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
305                            MAP_FILE, 0, 0);
306
307       /* XXX: what if mmap() has failed! */
308       VGLDisplay->Type = VIDBUF8;       /* XXX */
309       switch (VGLModeInfo.vi_mem_model) {
310       case V_INFO_MM_PLANAR:
311         if (VGLModeInfo.vi_depth == 4 && VGLModeInfo.vi_planes == 4) {
312           if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
313             VGLDisplay->Type = VIDBUF4S;
314           else
315             VGLDisplay->Type = VIDBUF4;
316         } else {
317           /* shouldn't be happening */
318         }
319         break;
320       case V_INFO_MM_PACKED:
321         if (VGLModeInfo.vi_depth == 8) {
322           if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
323             VGLDisplay->Type = VIDBUF8S;
324           else
325             VGLDisplay->Type = VIDBUF8;
326         } else {
327           /* shouldn't be happening */
328         }
329         break;
330       case V_INFO_MM_VGAX:
331         VGLDisplay->Type = VIDBUF8X;
332         break;
333       default:
334         /* shouldn't be happening */
335         break;
336       }
337
338       VGLDisplay->Bitmap = VGLMem;
339       VGLDisplay->Xsize = VGLModeInfo.vi_width;
340       VGLDisplay->Ysize = VGLModeInfo.vi_height;
341       VGLSetVScreenSize(VGLDisplay, VGLDisplay->VXsize, VGLDisplay->VYsize);
342       VGLPanScreen(VGLDisplay, VGLDisplay->Xorigin, VGLDisplay->Yorigin);
343       switch (VGLDisplay->Type) {
344       case VIDBUF4S:
345         outb(0x3c6, 0xff);
346         outb(0x3ce, 0x01); outb(0x3cf, 0x00);           /* set/reset enable */
347         outb(0x3ce, 0x08); outb(0x3cf, 0xff);           /* bit mask */
348         for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
349              offset += len) {
350           VGLSetSegment(offset);
351           len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
352                     VGLAdpInfo.va_window_size);
353           for (i = 0; i < VGLModeInfo.vi_planes; i++) {
354             outb(0x3c4, 0x02);
355             outb(0x3c5, 0x01<<i);
356             bcopy(&VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
357                   VGLMem, len);
358           }
359         }
360         break;
361       case VIDBUF4:
362       case VIDBUF8X:
363         outb(0x3c6, 0xff);
364         outb(0x3ce, 0x01); outb(0x3cf, 0x00);           /* set/reset enable */
365         outb(0x3ce, 0x08); outb(0x3cf, 0xff);           /* bit mask */
366         for (i = 0; i < VGLModeInfo.vi_planes; i++) {
367           outb(0x3c4, 0x02);
368           outb(0x3c5, 0x01<<i);
369           bcopy(&VGLBuf[i*VGLAdpInfo.va_window_size], VGLMem,
370                 VGLAdpInfo.va_window_size);
371         }
372         break;
373       case VIDBUF8:
374       case VIDBUF8S:
375         for (offset = 0; offset < VGLBufSize; offset += len) {
376           VGLSetSegment(offset);
377           len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
378           bcopy(&VGLBuf[offset], VGLMem, len);
379         }
380         break;
381       }
382       VGLRestorePalette();
383       ioctl(0, VT_RELDISP, VT_ACKACQ);
384     }
385     else {
386       switch (VGLDisplay->Type) {
387       case VIDBUF4S:
388         for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
389              offset += len) {
390           VGLSetSegment(offset);
391           len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
392                     VGLAdpInfo.va_window_size);
393           for (i = 0; i < VGLModeInfo.vi_planes; i++) {
394             outb(0x3ce, 0x04);
395             outb(0x3cf, i);
396             bcopy(VGLMem, &VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
397                   len);
398           }
399         }
400         break;
401       case VIDBUF4:
402       case VIDBUF8X:
403         /*
404          * NOTE: the saved buffer is NOT in the MEMBUF format which 
405          * the ordinary memory bitmap object is stored in. XXX
406          */
407         for (i = 0; i < VGLModeInfo.vi_planes; i++) {
408           outb(0x3ce, 0x04);
409           outb(0x3cf, i);
410           bcopy(VGLMem, &VGLBuf[i*VGLAdpInfo.va_window_size],
411                 VGLAdpInfo.va_window_size);
412         }
413         break;
414       case VIDBUF8:
415       case VIDBUF8S:
416         for (offset = 0; offset < VGLBufSize; offset += len) {
417           VGLSetSegment(offset);
418           len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
419           bcopy(VGLMem, &VGLBuf[offset], len);
420         }
421         break;
422       }
423       VGLMem = MAP_FAILED;
424       munmap(VGLDisplay->Bitmap, VGLAdpInfo.va_window_size);
425       ioctl(0, VGLOldMode, 0);
426       ioctl(0, KDSETMODE, KD_TEXT);
427       ioctl(0, KDDISABIO, 0);
428       ioctl(0, VT_RELDISP, VT_TRUE);
429       VGLDisplay->Bitmap = VGLBuf;
430       VGLDisplay->Type = MEMBUF;
431       VGLDisplay->Xsize = VGLDisplay->VXsize;
432       VGLDisplay->Ysize = VGLDisplay->VYsize;
433       while (!VGLOnDisplay) pause();
434     }
435   }
436 }
437
438 int
439 VGLSetSegment(unsigned int offset)
440 {
441   if (offset/VGLAdpInfo.va_window_size != VGLCurWindow) {
442     ioctl(0, CONS_SETWINORG, offset);           /* FBIO_SETWINORG */
443     VGLCurWindow = offset/VGLAdpInfo.va_window_size;
444   }
445   return (offset%VGLAdpInfo.va_window_size);
446 }
447
448 int
449 VGLSetVScreenSize(VGLBitmap *object, int VXsize, int VYsize)
450 {
451   if (VXsize < object->Xsize || VYsize < object->Ysize)
452     return -1;
453   if (object->Type == MEMBUF)
454     return -1;
455   if (ioctl(0, FBIO_SETLINEWIDTH, &VXsize))
456     return -1;
457   ioctl(0, CONS_ADPINFO, &VGLAdpInfo);  /* FBIO_ADPINFO */
458   object->VXsize = VGLAdpInfo.va_line_width
459                            *8/(VGLModeInfo.vi_depth/VGLModeInfo.vi_planes);
460   object->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
461   if (VYsize < object->VYsize)
462     object->VYsize = VYsize;
463
464 #ifdef LIBVGL_DEBUG
465   fprintf(stderr, "new size: VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
466           object->Xsize, object->Ysize, object->VXsize, object->VYsize);
467 #endif
468
469   return 0;
470 }
471
472 int
473 VGLPanScreen(VGLBitmap *object, int x, int y)
474 {
475   video_display_start_t origin;
476
477   if (x < 0 || x + object->Xsize > object->VXsize
478       || y < 0 || y + object->Ysize > object->VYsize)
479     return -1;
480   if (object->Type == MEMBUF)
481     return 0;
482   origin.x = x;
483   origin.y = y;
484   if (ioctl(0, FBIO_SETDISPSTART, &origin))
485     return -1;
486   object->Xorigin = x;
487   object->Yorigin = y;
488
489 #ifdef LIBVGL_DEBUG
490   fprintf(stderr, "new origin: (%d, %d)\n", x, y);
491 #endif
492
493   return 0;
494 }