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