xref: /dflybsd-src/lib/libvgl/main.c (revision c6cf4f8f1ebc9e3fe2a8c566f08adfc86122c7bf)
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.2 2003/06/17 04:26:52 dillon 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()
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()
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()
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   if (IOCGROUP(mode) == 'V')	/* XXX: this is ugly */
148     VGLModeInfo.vi_mode = (mode & 0x0ff) + M_VESA_BASE;
149   else
150     VGLModeInfo.vi_mode = mode & 0x0ff;
151   if (ioctl(0, CONS_MODEINFO, &VGLModeInfo))	/* FBIO_MODEINFO */
152     return -1;
153 
154   /* If current mode is VESA_800x600 then save its geometry to restore later */
155   if ((VGLOldMode >= M_VESA_BASE) && (VGLOldMode == M_VESA_800x600))
156     if (ioctl(0, TIOCGWINSZ, &VGLOldWSize))
157       return -1;
158 
159   VGLDisplay = (VGLBitmap *)malloc(sizeof(VGLBitmap));
160   if (VGLDisplay == NULL)
161     return -2;
162 
163   if (ioctl(0, KDENABIO, 0)) {
164     free(VGLDisplay);
165     return -3;
166   }
167 
168   VGLInitDone = 1;
169 
170   /*
171    * vi_mem_model specifies the memory model of the current video mode
172    * in -CURRENT.
173    */
174   switch (VGLModeInfo.vi_mem_model) {
175   case V_INFO_MM_PLANAR:
176     /* we can handle EGA/VGA planner modes only */
177     if (VGLModeInfo.vi_depth != 4 || VGLModeInfo.vi_planes != 4
178 	|| (adptype != KD_EGA && adptype != KD_VGA)) {
179       VGLEnd();
180       return -4;
181     }
182     VGLDisplay->Type = VIDBUF4;
183     break;
184   case V_INFO_MM_PACKED:
185     /* we can do only 256 color packed modes */
186     if (VGLModeInfo.vi_depth != 8) {
187       VGLEnd();
188       return -4;
189     }
190     VGLDisplay->Type = VIDBUF8;
191     break;
192   case V_INFO_MM_VGAX:
193     VGLDisplay->Type = VIDBUF8X;
194     break;
195   default:
196     VGLEnd();
197     return -4;
198   }
199 
200   ioctl(0, VT_WAITACTIVE, 0);
201   ioctl(0, KDSETMODE, KD_GRAPHICS);
202   if (ioctl(0, mode, 0)) {
203     VGLEnd();
204     return -5;
205   }
206   if (ioctl(0, CONS_ADPINFO, &VGLAdpInfo)) {	/* FBIO_ADPINFO */
207     VGLEnd();
208     return -6;
209   }
210 
211   /*
212    * Calculate the shadow screen buffer size.  In -CURRENT, va_buffer_size
213    * always holds the entire frame buffer size, wheather it's in the linear
214    * mode or windowed mode.
215    *     VGLBufSize = VGLAdpInfo.va_buffer_size;
216    * In -STABLE, va_buffer_size holds the frame buffer size, only if
217    * the linear frame buffer mode is supported. Otherwise the field is zero.
218    * We shall calculate the minimal size in this case:
219    *     VGLAdpInfo.va_line_width*VGLModeInfo.vi_height*VGLModeInfo.vi_planes
220    * or
221    *     VGLAdpInfo.va_window_size*VGLModeInfo.vi_planes;
222    * Use whichever is larger.
223    */
224   if (VGLAdpInfo.va_buffer_size != 0)
225     VGLBufSize = VGLAdpInfo.va_buffer_size;
226   else
227     VGLBufSize = max(VGLAdpInfo.va_line_width*VGLModeInfo.vi_height,
228 		     VGLAdpInfo.va_window_size)*VGLModeInfo.vi_planes;
229   VGLBuf = malloc(VGLBufSize);
230   if (VGLBuf == NULL) {
231     VGLEnd();
232     return -7;
233   }
234 
235 #ifdef LIBVGL_DEBUG
236   fprintf(stderr, "VGLBufSize:0x%x\n", VGLBufSize);
237 #endif
238 
239   /* see if we are in the windowed buffer mode or in the linear buffer mode */
240   if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) {
241     if (VGLDisplay->Type == VIDBUF4)
242       VGLDisplay->Type = VIDBUF4S;
243     else if (VGLDisplay->Type == VIDBUF8)
244       VGLDisplay->Type = VIDBUF8S;
245   }
246 
247   VGLMode = mode;
248   VGLCurWindow = 0;
249 
250   VGLDisplay->Xsize = VGLModeInfo.vi_width;
251   VGLDisplay->Ysize = VGLModeInfo.vi_height;
252   VGLDisplay->VXsize = VGLAdpInfo.va_line_width
253 			   *8/(VGLModeInfo.vi_depth/VGLModeInfo.vi_planes);
254   VGLDisplay->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
255   VGLDisplay->Xorigin = 0;
256   VGLDisplay->Yorigin = 0;
257 
258   VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
259 		       MAP_FILE, 0, 0);
260   if (VGLMem == MAP_FAILED) {
261     VGLEnd();
262     return -7;
263   }
264   VGLDisplay->Bitmap = VGLMem;
265 
266   VGLSavePalette();
267 
268 #ifdef LIBVGL_DEBUG
269   fprintf(stderr, "va_line_width:%d\n", VGLAdpInfo.va_line_width);
270   fprintf(stderr, "VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
271 	  VGLDisplay->Xsize, VGLDisplay->Ysize,
272 	  VGLDisplay->VXsize, VGLDisplay->VYsize);
273 #endif
274 
275   smode.mode = VT_PROCESS;
276   smode.waitv = 0;
277   smode.relsig = SIGUSR1;
278   smode.acqsig = SIGUSR1;
279   smode.frsig  = SIGINT;
280   if (ioctl(0, VT_SETMODE, &smode)) {
281     VGLEnd();
282     return -9;
283   }
284   VGLTextSetFontFile((byte*)0);
285   VGLClear(VGLDisplay, 0);
286   return 0;
287 }
288 
289 void
290 VGLCheckSwitch()
291 {
292   if (VGLAbortPending) {
293     VGLEnd();
294     exit(0);
295   }
296   while (VGLSwitchPending) {
297     unsigned int offset;
298     unsigned int len;
299     int i;
300 
301     VGLSwitchPending = 0;
302     if (VGLOnDisplay) {
303       ioctl(0, KDENABIO, 0);
304       ioctl(0, KDSETMODE, KD_GRAPHICS);
305       ioctl(0, VGLMode, 0);
306       VGLCurWindow = 0;
307       VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
308 			   MAP_FILE, 0, 0);
309 
310       /* XXX: what if mmap() has failed! */
311       VGLDisplay->Type = VIDBUF8;	/* XXX */
312       switch (VGLModeInfo.vi_mem_model) {
313       case V_INFO_MM_PLANAR:
314 	if (VGLModeInfo.vi_depth == 4 && VGLModeInfo.vi_planes == 4) {
315 	  if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
316 	    VGLDisplay->Type = VIDBUF4S;
317 	  else
318 	    VGLDisplay->Type = VIDBUF4;
319 	} else {
320 	  /* shouldn't be happening */
321 	}
322         break;
323       case V_INFO_MM_PACKED:
324 	if (VGLModeInfo.vi_depth == 8) {
325 	  if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
326 	    VGLDisplay->Type = VIDBUF8S;
327 	  else
328 	    VGLDisplay->Type = VIDBUF8;
329 	} else {
330 	  /* shouldn't be happening */
331 	}
332         break;
333       case V_INFO_MM_VGAX:
334 	VGLDisplay->Type = VIDBUF8X;
335 	break;
336       default:
337 	/* shouldn't be happening */
338         break;
339       }
340 
341       VGLDisplay->Bitmap = VGLMem;
342       VGLDisplay->Xsize = VGLModeInfo.vi_width;
343       VGLDisplay->Ysize = VGLModeInfo.vi_height;
344       VGLSetVScreenSize(VGLDisplay, VGLDisplay->VXsize, VGLDisplay->VYsize);
345       VGLPanScreen(VGLDisplay, VGLDisplay->Xorigin, VGLDisplay->Yorigin);
346       switch (VGLDisplay->Type) {
347       case VIDBUF4S:
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 (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
352 	     offset += len) {
353 	  VGLSetSegment(offset);
354 	  len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
355 		    VGLAdpInfo.va_window_size);
356 	  for (i = 0; i < VGLModeInfo.vi_planes; i++) {
357 	    outb(0x3c4, 0x02);
358 	    outb(0x3c5, 0x01<<i);
359 	    bcopy(&VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
360 		  VGLMem, len);
361 	  }
362 	}
363 	break;
364       case VIDBUF4:
365       case VIDBUF8X:
366 	outb(0x3c6, 0xff);
367 	outb(0x3ce, 0x01); outb(0x3cf, 0x00);		/* set/reset enable */
368 	outb(0x3ce, 0x08); outb(0x3cf, 0xff);		/* bit mask */
369 	for (i = 0; i < VGLModeInfo.vi_planes; i++) {
370 	  outb(0x3c4, 0x02);
371 	  outb(0x3c5, 0x01<<i);
372 	  bcopy(&VGLBuf[i*VGLAdpInfo.va_window_size], VGLMem,
373 		VGLAdpInfo.va_window_size);
374 	}
375 	break;
376       case VIDBUF8:
377       case VIDBUF8S:
378 	for (offset = 0; offset < VGLBufSize; offset += len) {
379 	  VGLSetSegment(offset);
380 	  len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
381           bcopy(&VGLBuf[offset], VGLMem, len);
382 	}
383 	break;
384       }
385       VGLRestorePalette();
386       ioctl(0, VT_RELDISP, VT_ACKACQ);
387     }
388     else {
389       switch (VGLDisplay->Type) {
390       case VIDBUF4S:
391 	for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
392 	     offset += len) {
393 	  VGLSetSegment(offset);
394 	  len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
395 		    VGLAdpInfo.va_window_size);
396 	  for (i = 0; i < VGLModeInfo.vi_planes; i++) {
397 	    outb(0x3ce, 0x04);
398 	    outb(0x3cf, i);
399 	    bcopy(VGLMem, &VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
400 		  len);
401 	  }
402 	}
403 	break;
404       case VIDBUF4:
405       case VIDBUF8X:
406 	/*
407 	 * NOTE: the saved buffer is NOT in the MEMBUF format which
408 	 * the ordinary memory bitmap object is stored in. XXX
409 	 */
410 	for (i = 0; i < VGLModeInfo.vi_planes; i++) {
411 	  outb(0x3ce, 0x04);
412 	  outb(0x3cf, i);
413 	  bcopy(VGLMem, &VGLBuf[i*VGLAdpInfo.va_window_size],
414 		VGLAdpInfo.va_window_size);
415 	}
416 	break;
417       case VIDBUF8:
418       case VIDBUF8S:
419 	for (offset = 0; offset < VGLBufSize; offset += len) {
420 	  VGLSetSegment(offset);
421 	  len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
422           bcopy(VGLMem, &VGLBuf[offset], len);
423 	}
424 	break;
425       }
426       VGLMem = MAP_FAILED;
427       munmap(VGLDisplay->Bitmap, VGLAdpInfo.va_window_size);
428       ioctl(0, VGLOldMode, 0);
429       ioctl(0, KDSETMODE, KD_TEXT);
430       ioctl(0, KDDISABIO, 0);
431       ioctl(0, VT_RELDISP, VT_TRUE);
432       VGLDisplay->Bitmap = VGLBuf;
433       VGLDisplay->Type = MEMBUF;
434       VGLDisplay->Xsize = VGLDisplay->VXsize;
435       VGLDisplay->Ysize = VGLDisplay->VYsize;
436       while (!VGLOnDisplay) pause();
437     }
438   }
439 }
440 
441 int
442 VGLSetSegment(unsigned int offset)
443 {
444   if (offset/VGLAdpInfo.va_window_size != VGLCurWindow) {
445     ioctl(0, CONS_SETWINORG, offset);		/* FBIO_SETWINORG */
446     VGLCurWindow = offset/VGLAdpInfo.va_window_size;
447   }
448   return (offset%VGLAdpInfo.va_window_size);
449 }
450 
451 int
452 VGLSetVScreenSize(VGLBitmap *object, int VXsize, int VYsize)
453 {
454   if (VXsize < object->Xsize || VYsize < object->Ysize)
455     return -1;
456   if (object->Type == MEMBUF)
457     return -1;
458   if (ioctl(0, FBIO_SETLINEWIDTH, &VXsize))
459     return -1;
460   ioctl(0, CONS_ADPINFO, &VGLAdpInfo);	/* FBIO_ADPINFO */
461   object->VXsize = VGLAdpInfo.va_line_width
462 			   *8/(VGLModeInfo.vi_depth/VGLModeInfo.vi_planes);
463   object->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
464   if (VYsize < object->VYsize)
465     object->VYsize = VYsize;
466 
467 #ifdef LIBVGL_DEBUG
468   fprintf(stderr, "new size: VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
469 	  object->Xsize, object->Ysize, object->VXsize, object->VYsize);
470 #endif
471 
472   return 0;
473 }
474 
475 int
476 VGLPanScreen(VGLBitmap *object, int x, int y)
477 {
478   video_display_start_t origin;
479 
480   if (x < 0 || x + object->Xsize > object->VXsize
481       || y < 0 || y + object->Ysize > object->VYsize)
482     return -1;
483   if (object->Type == MEMBUF)
484     return 0;
485   origin.x = x;
486   origin.y = y;
487   if (ioctl(0, FBIO_SETDISPSTART, &origin))
488     return -1;
489   object->Xorigin = x;
490   object->Yorigin = y;
491 
492 #ifdef LIBVGL_DEBUG
493   fprintf(stderr, "new origin: (%d, %d)\n", x, y);
494 #endif
495 
496   return 0;
497 }
498