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