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