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