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