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