xref: /freebsd/lib/libvgl/main.c (revision 79483833d06343afe6b87b59b1332e186d1339f8)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1991-1997 Søren Schmidt
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer
12  *    in this position and unchanged.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <stdio.h>
35 #include <sys/types.h>
36 #include <sys/signal.h>
37 #include <sys/file.h>
38 #include <sys/ioctl.h>
39 #include <sys/mman.h>
40 #include <sys/fbio.h>
41 #include <sys/kbio.h>
42 #include <sys/consio.h>
43 #include "vgl.h"
44 
45 /* XXX Direct Color 24bits modes unsupported */
46 
47 #define min(x, y)	(((x) < (y)) ? (x) : (y))
48 #define max(x, y)	(((x) > (y)) ? (x) : (y))
49 
50 VGLBitmap *VGLDisplay;
51 video_info_t VGLModeInfo;
52 video_adapter_info_t VGLAdpInfo;
53 byte *VGLBuf;
54 
55 static int VGLMode;
56 static int VGLOldMode;
57 static size_t VGLBufSize;
58 static byte *VGLMem = MAP_FAILED;
59 static int VGLSwitchPending;
60 static int VGLAbortPending;
61 static int VGLOnDisplay;
62 static unsigned int VGLCurWindow;
63 static int VGLInitDone = 0;
64 static vid_info_t VGLOldVInfo;
65 
66 void
67 VGLEnd()
68 {
69 struct vt_mode smode;
70 
71   if (!VGLInitDone)
72     return;
73   VGLInitDone = 0;
74   VGLSwitchPending = 0;
75   VGLAbortPending = 0;
76 
77   signal(SIGUSR1, SIG_IGN);
78 
79   if (VGLMem != MAP_FAILED) {
80     VGLClear(VGLDisplay, 0);
81     munmap(VGLMem, VGLAdpInfo.va_window_size);
82   }
83 
84   if (VGLOldMode >= M_VESA_BASE) {
85     /* ugly, but necessary */
86     ioctl(0, _IO('V', VGLOldMode - M_VESA_BASE), 0);
87     if (VGLOldMode == M_VESA_800x600) {
88       int size[3];
89       size[0] = VGLOldVInfo.mv_csz;
90       size[1] = VGLOldVInfo.mv_rsz;
91       size[2] = 16;
92       ioctl(0, KDRASTER, size);
93     }
94   } else {
95     ioctl(0, _IO('S', VGLOldMode), 0);
96   }
97   ioctl(0, KDDISABIO, 0);
98   ioctl(0, KDSETMODE, KD_TEXT);
99   smode.mode = VT_AUTO;
100   ioctl(0, VT_SETMODE, &smode);
101   if (VGLBuf)
102     free(VGLBuf);
103   VGLBuf = NULL;
104   free(VGLDisplay);
105   VGLDisplay = NULL;
106   VGLKeyboardEnd();
107 }
108 
109 static void
110 VGLAbort(int arg __unused)
111 {
112   VGLAbortPending = 1;
113   signal(SIGINT, SIG_IGN);
114   signal(SIGTERM, SIG_IGN);
115   signal(SIGSEGV, SIG_IGN);
116   signal(SIGBUS, SIG_IGN);
117   signal(SIGUSR2, SIG_IGN);
118 }
119 
120 static void
121 VGLSwitch(int arg __unused)
122 {
123   if (!VGLOnDisplay)
124     VGLOnDisplay = 1;
125   else
126     VGLOnDisplay = 0;
127   VGLSwitchPending = 1;
128   signal(SIGUSR1, VGLSwitch);
129 }
130 
131 int
132 VGLInit(int mode)
133 {
134   struct vt_mode smode;
135   int adptype, depth;
136 
137   if (VGLInitDone)
138     return -1;
139 
140   signal(SIGUSR1, VGLSwitch);
141   signal(SIGINT, VGLAbort);
142   signal(SIGTERM, VGLAbort);
143   signal(SIGSEGV, VGLAbort);
144   signal(SIGBUS, VGLAbort);
145   signal(SIGUSR2, SIG_IGN);
146 
147   VGLOnDisplay = 1;
148   VGLSwitchPending = 0;
149   VGLAbortPending = 0;
150 
151   if (ioctl(0, CONS_GET, &VGLOldMode) || ioctl(0, CONS_CURRENT, &adptype))
152     return -1;
153   if (IOCGROUP(mode) == 'V')	/* XXX: this is ugly */
154     VGLModeInfo.vi_mode = (mode & 0x0ff) + M_VESA_BASE;
155   else
156     VGLModeInfo.vi_mode = mode & 0x0ff;
157   if (ioctl(0, CONS_MODEINFO, &VGLModeInfo))	/* FBIO_MODEINFO */
158     return -1;
159 
160   /* If current mode is VESA_800x600 then save its geometry to restore later */
161   if ((VGLOldMode >= M_VESA_BASE) && (VGLOldMode == M_VESA_800x600)) {
162     VGLOldVInfo.size = sizeof(VGLOldVInfo);
163     if (ioctl(0, CONS_GETINFO, &VGLOldVInfo))
164       return -1;
165   }
166 
167   VGLDisplay = (VGLBitmap *)malloc(sizeof(VGLBitmap));
168   if (VGLDisplay == NULL)
169     return -2;
170 
171   if (ioctl(0, KDENABIO, 0)) {
172     free(VGLDisplay);
173     return -3;
174   }
175 
176   VGLInitDone = 1;
177 
178   /*
179    * vi_mem_model specifies the memory model of the current video mode
180    * in -CURRENT.
181    */
182   switch (VGLModeInfo.vi_mem_model) {
183   case V_INFO_MM_PLANAR:
184     /* we can handle EGA/VGA planner modes only */
185     if (VGLModeInfo.vi_depth != 4 || VGLModeInfo.vi_planes != 4
186 	|| (adptype != KD_EGA && adptype != KD_VGA)) {
187       VGLEnd();
188       return -4;
189     }
190     VGLDisplay->Type = VIDBUF4;
191     VGLDisplay->PixelBytes = 1;
192     break;
193   case V_INFO_MM_PACKED:
194     /* we can do only 256 color packed modes */
195     if (VGLModeInfo.vi_depth != 8) {
196       VGLEnd();
197       return -4;
198     }
199     VGLDisplay->Type = VIDBUF8;
200     VGLDisplay->PixelBytes = 1;
201     break;
202   case V_INFO_MM_VGAX:
203     VGLDisplay->Type = VIDBUF8X;
204     VGLDisplay->PixelBytes = 1;
205     break;
206   case V_INFO_MM_DIRECT:
207     VGLDisplay->PixelBytes = VGLModeInfo.vi_pixel_size;
208     switch (VGLDisplay->PixelBytes) {
209     case 2:
210       VGLDisplay->Type = VIDBUF16;
211       break;
212 #if notyet
213     case 3:
214       VGLDisplay->Type = VIDBUF24;
215       break;
216 #endif
217     case 4:
218       VGLDisplay->Type = VIDBUF32;
219       break;
220     default:
221       VGLEnd();
222       return -4;
223     }
224     break;
225   default:
226     VGLEnd();
227     return -4;
228   }
229 
230   ioctl(0, VT_WAITACTIVE, 0);
231   ioctl(0, KDSETMODE, KD_GRAPHICS);
232   if (ioctl(0, mode, 0)) {
233     VGLEnd();
234     return -5;
235   }
236   if (ioctl(0, CONS_ADPINFO, &VGLAdpInfo)) {	/* FBIO_ADPINFO */
237     VGLEnd();
238     return -6;
239   }
240 
241   /*
242    * Calculate the shadow screen buffer size.  In -CURRENT, va_buffer_size
243    * always holds the entire frame buffer size, wheather it's in the linear
244    * mode or windowed mode.
245    *     VGLBufSize = VGLAdpInfo.va_buffer_size;
246    * In -STABLE, va_buffer_size holds the frame buffer size, only if
247    * the linear frame buffer mode is supported. Otherwise the field is zero.
248    * We shall calculate the minimal size in this case:
249    *     VGLAdpInfo.va_line_width*VGLModeInfo.vi_height*VGLModeInfo.vi_planes
250    * or
251    *     VGLAdpInfo.va_window_size*VGLModeInfo.vi_planes;
252    * Use whichever is larger.
253    */
254   if (VGLAdpInfo.va_buffer_size != 0)
255     VGLBufSize = VGLAdpInfo.va_buffer_size;
256   else
257     VGLBufSize = max(VGLAdpInfo.va_line_width*VGLModeInfo.vi_height,
258 		     VGLAdpInfo.va_window_size)*VGLModeInfo.vi_planes;
259   VGLBuf = malloc(VGLBufSize);
260   if (VGLBuf == NULL) {
261     VGLEnd();
262     return -7;
263   }
264 
265 #ifdef LIBVGL_DEBUG
266   fprintf(stderr, "VGLBufSize:0x%x\n", VGLBufSize);
267 #endif
268 
269   /* see if we are in the windowed buffer mode or in the linear buffer mode */
270   if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) {
271     switch (VGLDisplay->Type) {
272     case VIDBUF4:
273       VGLDisplay->Type = VIDBUF4S;
274       break;
275     case VIDBUF8:
276       VGLDisplay->Type = VIDBUF8S;
277       break;
278     case VIDBUF16:
279       VGLDisplay->Type = VIDBUF16S;
280       break;
281     case VIDBUF24:
282       VGLDisplay->Type = VIDBUF24S;
283       break;
284     case VIDBUF32:
285       VGLDisplay->Type = VIDBUF32S;
286       break;
287     default:
288       VGLEnd();
289       return -8;
290     }
291   }
292 
293   VGLMode = mode;
294   VGLCurWindow = 0;
295 
296   VGLDisplay->Xsize = VGLModeInfo.vi_width;
297   VGLDisplay->Ysize = VGLModeInfo.vi_height;
298   depth = VGLModeInfo.vi_depth;
299   if (depth == 15)
300     depth = 16;
301   VGLDisplay->VXsize = VGLAdpInfo.va_line_width
302 			   *8/(depth/VGLModeInfo.vi_planes);
303   VGLDisplay->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
304   VGLDisplay->Xorigin = 0;
305   VGLDisplay->Yorigin = 0;
306 
307   VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
308 		       MAP_FILE | MAP_SHARED, 0, 0);
309   if (VGLMem == MAP_FAILED) {
310     VGLEnd();
311     return -7;
312   }
313   VGLDisplay->Bitmap = VGLMem;
314 
315   VGLSavePalette();
316 
317 #ifdef LIBVGL_DEBUG
318   fprintf(stderr, "va_line_width:%d\n", VGLAdpInfo.va_line_width);
319   fprintf(stderr, "VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
320 	  VGLDisplay->Xsize, VGLDisplay->Ysize,
321 	  VGLDisplay->VXsize, VGLDisplay->VYsize);
322 #endif
323 
324   smode.mode = VT_PROCESS;
325   smode.waitv = 0;
326   smode.relsig = SIGUSR1;
327   smode.acqsig = SIGUSR1;
328   smode.frsig  = SIGINT;
329   if (ioctl(0, VT_SETMODE, &smode)) {
330     VGLEnd();
331     return -9;
332   }
333   VGLTextSetFontFile((byte*)0);
334   VGLClear(VGLDisplay, 0);
335   return 0;
336 }
337 
338 void
339 VGLCheckSwitch()
340 {
341   if (VGLAbortPending) {
342     VGLEnd();
343     exit(0);
344   }
345   while (VGLSwitchPending) {
346     unsigned int offset;
347     unsigned int len;
348     int i;
349 
350     VGLSwitchPending = 0;
351     if (VGLOnDisplay) {
352       ioctl(0, KDENABIO, 0);
353       ioctl(0, KDSETMODE, KD_GRAPHICS);
354       ioctl(0, VGLMode, 0);
355       VGLCurWindow = 0;
356       VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
357 			   MAP_FILE | MAP_SHARED, 0, 0);
358 
359       /* XXX: what if mmap() has failed! */
360       VGLDisplay->Type = VIDBUF8;	/* XXX */
361       switch (VGLModeInfo.vi_mem_model) {
362       case V_INFO_MM_PLANAR:
363 	if (VGLModeInfo.vi_depth == 4 && VGLModeInfo.vi_planes == 4) {
364 	  if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
365 	    VGLDisplay->Type = VIDBUF4S;
366 	  else
367 	    VGLDisplay->Type = VIDBUF4;
368 	} else {
369 	  /* shouldn't be happening */
370 	}
371         break;
372       case V_INFO_MM_PACKED:
373 	if (VGLModeInfo.vi_depth == 8) {
374 	  if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
375 	    VGLDisplay->Type = VIDBUF8S;
376 	  else
377 	    VGLDisplay->Type = VIDBUF8;
378 	}
379         break;
380       case V_INFO_MM_VGAX:
381 	VGLDisplay->Type = VIDBUF8X;
382 	break;
383       case V_INFO_MM_DIRECT:
384 	switch (VGLModeInfo.vi_pixel_size) {
385 	  case 2:
386 	    if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
387 	      VGLDisplay->Type = VIDBUF16S;
388 	    else
389 	      VGLDisplay->Type = VIDBUF16;
390 	    break;
391 	  case 3:
392 	    if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
393 	      VGLDisplay->Type = VIDBUF24S;
394 	    else
395 	      VGLDisplay->Type = VIDBUF24;
396 	    break;
397 	  case 4:
398 	    if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
399 	      VGLDisplay->Type = VIDBUF32S;
400 	    else
401 	      VGLDisplay->Type = VIDBUF32;
402 	    break;
403 	  default:
404 	  /* shouldn't be happening */
405           break;
406         }
407       default:
408 	/* shouldn't be happening */
409         break;
410       }
411 
412       VGLDisplay->Bitmap = VGLMem;
413       VGLDisplay->Xsize = VGLModeInfo.vi_width;
414       VGLDisplay->Ysize = VGLModeInfo.vi_height;
415       VGLSetVScreenSize(VGLDisplay, VGLDisplay->VXsize, VGLDisplay->VYsize);
416       VGLPanScreen(VGLDisplay, VGLDisplay->Xorigin, VGLDisplay->Yorigin);
417       switch (VGLDisplay->Type) {
418       case VIDBUF4S:
419 	outb(0x3c6, 0xff);
420 	outb(0x3ce, 0x01); outb(0x3cf, 0x00);		/* set/reset enable */
421 	outb(0x3ce, 0x08); outb(0x3cf, 0xff);		/* bit mask */
422 	for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
423 	     offset += len) {
424 	  VGLSetSegment(offset);
425 	  len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
426 		    VGLAdpInfo.va_window_size);
427 	  for (i = 0; i < VGLModeInfo.vi_planes; i++) {
428 	    outb(0x3c4, 0x02);
429 	    outb(0x3c5, 0x01<<i);
430 	    bcopy(&VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
431 		  VGLMem, len);
432 	  }
433 	}
434 	break;
435       case VIDBUF4:
436       case VIDBUF8X:
437 	outb(0x3c6, 0xff);
438 	outb(0x3ce, 0x01); outb(0x3cf, 0x00);		/* set/reset enable */
439 	outb(0x3ce, 0x08); outb(0x3cf, 0xff);		/* bit mask */
440 	for (i = 0; i < VGLModeInfo.vi_planes; i++) {
441 	  outb(0x3c4, 0x02);
442 	  outb(0x3c5, 0x01<<i);
443 	  bcopy(&VGLBuf[i*VGLAdpInfo.va_window_size], VGLMem,
444 		VGLAdpInfo.va_window_size);
445 	}
446 	break;
447       case VIDBUF8:
448       case VIDBUF8S:
449       case VIDBUF16:
450       case VIDBUF16S:
451       case VIDBUF24:
452       case VIDBUF24S:
453       case VIDBUF32:
454       case VIDBUF32S:
455 	for (offset = 0; offset < VGLBufSize; offset += len) {
456 	  VGLSetSegment(offset);
457 	  len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
458           bcopy(&VGLBuf[offset], VGLMem, len);
459 	}
460 	break;
461       }
462       VGLRestorePalette();
463       ioctl(0, VT_RELDISP, VT_ACKACQ);
464     }
465     else {
466       switch (VGLDisplay->Type) {
467       case VIDBUF4S:
468 	for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
469 	     offset += len) {
470 	  VGLSetSegment(offset);
471 	  len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
472 		    VGLAdpInfo.va_window_size);
473 	  for (i = 0; i < VGLModeInfo.vi_planes; i++) {
474 	    outb(0x3ce, 0x04);
475 	    outb(0x3cf, i);
476 	    bcopy(VGLMem, &VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
477 		  len);
478 	  }
479 	}
480 	break;
481       case VIDBUF4:
482       case VIDBUF8X:
483 	/*
484 	 * NOTE: the saved buffer is NOT in the MEMBUF format which
485 	 * the ordinary memory bitmap object is stored in. XXX
486 	 */
487 	for (i = 0; i < VGLModeInfo.vi_planes; i++) {
488 	  outb(0x3ce, 0x04);
489 	  outb(0x3cf, i);
490 	  bcopy(VGLMem, &VGLBuf[i*VGLAdpInfo.va_window_size],
491 		VGLAdpInfo.va_window_size);
492 	}
493 	break;
494       case VIDBUF8:
495       case VIDBUF8S:
496       case VIDBUF16:
497       case VIDBUF16S:
498       case VIDBUF24:
499       case VIDBUF24S:
500       case VIDBUF32:
501       case VIDBUF32S:
502 	for (offset = 0; offset < VGLBufSize; offset += len) {
503 	  VGLSetSegment(offset);
504 	  len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
505           bcopy(VGLMem, &VGLBuf[offset], len);
506 	}
507 	break;
508       }
509       VGLMem = MAP_FAILED;
510       munmap(VGLDisplay->Bitmap, VGLAdpInfo.va_window_size);
511       ioctl(0, VGLOldMode, 0);
512       ioctl(0, KDSETMODE, KD_TEXT);
513       ioctl(0, KDDISABIO, 0);
514       ioctl(0, VT_RELDISP, VT_TRUE);
515       VGLDisplay->Bitmap = VGLBuf;
516       VGLDisplay->Type = MEMBUF;
517       VGLDisplay->Xsize = VGLDisplay->VXsize;
518       VGLDisplay->Ysize = VGLDisplay->VYsize;
519       while (!VGLOnDisplay) pause();
520     }
521   }
522 }
523 
524 int
525 VGLSetSegment(unsigned int offset)
526 {
527   if (offset/VGLAdpInfo.va_window_size != VGLCurWindow) {
528     ioctl(0, CONS_SETWINORG, offset);		/* FBIO_SETWINORG */
529     VGLCurWindow = offset/VGLAdpInfo.va_window_size;
530   }
531   return (offset%VGLAdpInfo.va_window_size);
532 }
533 
534 int
535 VGLSetVScreenSize(VGLBitmap *object, int VXsize, int VYsize)
536 {
537   int depth;
538 
539   if (VXsize < object->Xsize || VYsize < object->Ysize)
540     return -1;
541   if (object->Type == MEMBUF)
542     return -1;
543   if (ioctl(0, FBIO_SETLINEWIDTH, &VXsize))
544     return -1;
545   ioctl(0, CONS_ADPINFO, &VGLAdpInfo);	/* FBIO_ADPINFO */
546   depth = VGLModeInfo.vi_depth;
547   if (depth == 15)
548     depth = 16;
549   object->VXsize = VGLAdpInfo.va_line_width
550 			   *8/(depth/VGLModeInfo.vi_planes);
551   object->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
552   if (VYsize < object->VYsize)
553     object->VYsize = VYsize;
554 
555 #ifdef LIBVGL_DEBUG
556   fprintf(stderr, "new size: VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
557 	  object->Xsize, object->Ysize, object->VXsize, object->VYsize);
558 #endif
559 
560   return 0;
561 }
562 
563 int
564 VGLPanScreen(VGLBitmap *object, int x, int y)
565 {
566   video_display_start_t origin;
567 
568   if (x < 0 || x + object->Xsize > object->VXsize
569       || y < 0 || y + object->Ysize > object->VYsize)
570     return -1;
571   if (object->Type == MEMBUF)
572     return 0;
573   origin.x = x;
574   origin.y = y;
575   if (ioctl(0, FBIO_SETDISPSTART, &origin))
576     return -1;
577   object->Xorigin = x;
578   object->Yorigin = y;
579 
580 #ifdef LIBVGL_DEBUG
581   fprintf(stderr, "new origin: (%d, %d)\n", x, y);
582 #endif
583 
584   return 0;
585 }
586