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