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