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