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