xref: /freebsd/lib/libvgl/main.c (revision b0fefb25c558179e9f9c7f0d375c6a03fb567eb9)
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 <signal.h>
35 #include <stdio.h>
36 #include <sys/types.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 video_info_t VGLOldModeInfo;
65 static vid_info_t VGLOldVInfo;
66 
67 void
68 VGLEnd()
69 {
70 struct vt_mode smode;
71   int size[3];
72 
73   if (!VGLInitDone)
74     return;
75   VGLInitDone = 0;
76   VGLSwitchPending = 0;
77   VGLAbortPending = 0;
78 
79   signal(SIGUSR1, SIG_IGN);
80 
81   if (VGLMem != MAP_FAILED) {
82     VGLClear(VGLDisplay, 0);
83     munmap(VGLMem, VGLAdpInfo.va_window_size);
84   }
85 
86   if (VGLOldMode >= M_VESA_BASE)
87     ioctl(0, _IO('V', VGLOldMode - M_VESA_BASE), 0);
88   else
89     ioctl(0, _IO('S', VGLOldMode), 0);
90   if (VGLOldModeInfo.vi_flags & V_INFO_GRAPHICS) {
91     size[0] = VGLOldVInfo.mv_csz;
92     size[1] = VGLOldVInfo.mv_rsz;
93     size[2] = VGLOldVInfo.font_size;;
94     ioctl(0, KDRASTER, size);
95   }
96   ioctl(0, KDDISABIO, 0);
97   ioctl(0, KDSETMODE, KD_TEXT);
98   smode.mode = VT_AUTO;
99   ioctl(0, VT_SETMODE, &smode);
100   if (VGLBuf)
101     free(VGLBuf);
102   VGLBuf = NULL;
103   free(VGLDisplay);
104   VGLDisplay = NULL;
105   VGLKeyboardEnd();
106 }
107 
108 static void
109 VGLAbort(int arg)
110 {
111   sigset_t mask;
112 
113   VGLAbortPending = 1;
114   signal(SIGINT, SIG_IGN);
115   signal(SIGTERM, SIG_IGN);
116   signal(SIGUSR2, SIG_IGN);
117   if (arg == SIGBUS || arg == SIGSEGV) {
118     signal(arg, SIG_DFL);
119     sigemptyset(&mask);
120     sigaddset(&mask, arg);
121     sigprocmask(SIG_UNBLOCK, &mask, NULL);
122     VGLEnd();
123     kill(getpid(), arg);
124   }
125 }
126 
127 static void
128 VGLSwitch(int arg __unused)
129 {
130   if (!VGLOnDisplay)
131     VGLOnDisplay = 1;
132   else
133     VGLOnDisplay = 0;
134   VGLSwitchPending = 1;
135   signal(SIGUSR1, VGLSwitch);
136 }
137 
138 int
139 VGLInit(int mode)
140 {
141   struct vt_mode smode;
142   int adptype, depth;
143 
144   if (VGLInitDone)
145     return -1;
146 
147   signal(SIGUSR1, VGLSwitch);
148   signal(SIGINT, VGLAbort);
149   signal(SIGTERM, VGLAbort);
150   signal(SIGSEGV, VGLAbort);
151   signal(SIGBUS, VGLAbort);
152   signal(SIGUSR2, SIG_IGN);
153 
154   VGLOnDisplay = 1;
155   VGLSwitchPending = 0;
156   VGLAbortPending = 0;
157 
158   if (ioctl(0, CONS_GET, &VGLOldMode) || ioctl(0, CONS_CURRENT, &adptype))
159     return -1;
160   if (IOCGROUP(mode) == 'V')	/* XXX: this is ugly */
161     VGLModeInfo.vi_mode = (mode & 0x0ff) + M_VESA_BASE;
162   else
163     VGLModeInfo.vi_mode = mode & 0x0ff;
164   if (ioctl(0, CONS_MODEINFO, &VGLModeInfo))	/* FBIO_MODEINFO */
165     return -1;
166 
167   /* Save info for old mode to restore font size if old mode is graphics. */
168   VGLOldModeInfo.vi_mode = VGLOldMode;
169   if (ioctl(0, CONS_MODEINFO, &VGLOldModeInfo))
170     return -1;
171   VGLOldVInfo.size = sizeof(VGLOldVInfo);
172   if (ioctl(0, CONS_GETINFO, &VGLOldVInfo))
173     return -1;
174 
175   VGLDisplay = (VGLBitmap *)malloc(sizeof(VGLBitmap));
176   if (VGLDisplay == NULL)
177     return -2;
178 
179   if (ioctl(0, KDENABIO, 0)) {
180     free(VGLDisplay);
181     return -3;
182   }
183 
184   VGLInitDone = 1;
185 
186   /*
187    * vi_mem_model specifies the memory model of the current video mode
188    * in -CURRENT.
189    */
190   switch (VGLModeInfo.vi_mem_model) {
191   case V_INFO_MM_PLANAR:
192     /* we can handle EGA/VGA planner modes only */
193     if (VGLModeInfo.vi_depth != 4 || VGLModeInfo.vi_planes != 4
194 	|| (adptype != KD_EGA && adptype != KD_VGA)) {
195       VGLEnd();
196       return -4;
197     }
198     VGLDisplay->Type = VIDBUF4;
199     VGLDisplay->PixelBytes = 1;
200     break;
201   case V_INFO_MM_PACKED:
202     /* we can do only 256 color packed modes */
203     if (VGLModeInfo.vi_depth != 8) {
204       VGLEnd();
205       return -4;
206     }
207     VGLDisplay->Type = VIDBUF8;
208     VGLDisplay->PixelBytes = 1;
209     break;
210   case V_INFO_MM_VGAX:
211     VGLDisplay->Type = VIDBUF8X;
212     VGLDisplay->PixelBytes = 1;
213     break;
214   case V_INFO_MM_DIRECT:
215     VGLDisplay->PixelBytes = VGLModeInfo.vi_pixel_size;
216     switch (VGLDisplay->PixelBytes) {
217     case 2:
218       VGLDisplay->Type = VIDBUF16;
219       break;
220 #if notyet
221     case 3:
222       VGLDisplay->Type = VIDBUF24;
223       break;
224 #endif
225     case 4:
226       VGLDisplay->Type = VIDBUF32;
227       break;
228     default:
229       VGLEnd();
230       return -4;
231     }
232     break;
233   default:
234     VGLEnd();
235     return -4;
236   }
237 
238   ioctl(0, VT_WAITACTIVE, 0);
239   ioctl(0, KDSETMODE, KD_GRAPHICS);
240   if (ioctl(0, mode, 0)) {
241     VGLEnd();
242     return -5;
243   }
244   if (ioctl(0, CONS_ADPINFO, &VGLAdpInfo)) {	/* FBIO_ADPINFO */
245     VGLEnd();
246     return -6;
247   }
248 
249   /*
250    * Calculate the shadow screen buffer size.  In -CURRENT, va_buffer_size
251    * always holds the entire frame buffer size, wheather it's in the linear
252    * mode or windowed mode.
253    *     VGLBufSize = VGLAdpInfo.va_buffer_size;
254    * In -STABLE, va_buffer_size holds the frame buffer size, only if
255    * the linear frame buffer mode is supported. Otherwise the field is zero.
256    * We shall calculate the minimal size in this case:
257    *     VGLAdpInfo.va_line_width*VGLModeInfo.vi_height*VGLModeInfo.vi_planes
258    * or
259    *     VGLAdpInfo.va_window_size*VGLModeInfo.vi_planes;
260    * Use whichever is larger.
261    */
262   if (VGLAdpInfo.va_buffer_size != 0)
263     VGLBufSize = VGLAdpInfo.va_buffer_size;
264   else
265     VGLBufSize = max(VGLAdpInfo.va_line_width*VGLModeInfo.vi_height,
266 		     VGLAdpInfo.va_window_size)*VGLModeInfo.vi_planes;
267   VGLBuf = malloc(VGLBufSize);
268   if (VGLBuf == NULL) {
269     VGLEnd();
270     return -7;
271   }
272 
273 #ifdef LIBVGL_DEBUG
274   fprintf(stderr, "VGLBufSize:0x%x\n", VGLBufSize);
275 #endif
276 
277   /* see if we are in the windowed buffer mode or in the linear buffer mode */
278   if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) {
279     switch (VGLDisplay->Type) {
280     case VIDBUF4:
281       VGLDisplay->Type = VIDBUF4S;
282       break;
283     case VIDBUF8:
284       VGLDisplay->Type = VIDBUF8S;
285       break;
286     case VIDBUF16:
287       VGLDisplay->Type = VIDBUF16S;
288       break;
289     case VIDBUF24:
290       VGLDisplay->Type = VIDBUF24S;
291       break;
292     case VIDBUF32:
293       VGLDisplay->Type = VIDBUF32S;
294       break;
295     default:
296       VGLEnd();
297       return -8;
298     }
299   }
300 
301   VGLMode = mode;
302   VGLCurWindow = 0;
303 
304   VGLDisplay->Xsize = VGLModeInfo.vi_width;
305   VGLDisplay->Ysize = VGLModeInfo.vi_height;
306   depth = VGLModeInfo.vi_depth;
307   if (depth == 15)
308     depth = 16;
309   VGLDisplay->VXsize = VGLAdpInfo.va_line_width
310 			   *8/(depth/VGLModeInfo.vi_planes);
311   VGLDisplay->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
312   VGLDisplay->Xorigin = 0;
313   VGLDisplay->Yorigin = 0;
314 
315   VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
316 		       MAP_FILE | MAP_SHARED, 0, 0);
317   if (VGLMem == MAP_FAILED) {
318     VGLEnd();
319     return -7;
320   }
321   VGLDisplay->Bitmap = VGLMem;
322 
323   VGLSavePalette();
324 
325 #ifdef LIBVGL_DEBUG
326   fprintf(stderr, "va_line_width:%d\n", VGLAdpInfo.va_line_width);
327   fprintf(stderr, "VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
328 	  VGLDisplay->Xsize, VGLDisplay->Ysize,
329 	  VGLDisplay->VXsize, VGLDisplay->VYsize);
330 #endif
331 
332   smode.mode = VT_PROCESS;
333   smode.waitv = 0;
334   smode.relsig = SIGUSR1;
335   smode.acqsig = SIGUSR1;
336   smode.frsig  = SIGINT;
337   if (ioctl(0, VT_SETMODE, &smode)) {
338     VGLEnd();
339     return -9;
340   }
341   VGLTextSetFontFile((byte*)0);
342   VGLClear(VGLDisplay, 0);
343   return 0;
344 }
345 
346 void
347 VGLCheckSwitch()
348 {
349   if (VGLAbortPending) {
350     VGLEnd();
351     exit(0);
352   }
353   while (VGLSwitchPending) {
354     unsigned int offset;
355     unsigned int len;
356     int i;
357 
358     VGLSwitchPending = 0;
359     if (VGLOnDisplay) {
360       ioctl(0, KDENABIO, 0);
361       ioctl(0, KDSETMODE, KD_GRAPHICS);
362       ioctl(0, VGLMode, 0);
363       VGLCurWindow = 0;
364       VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
365 			   MAP_FILE | MAP_SHARED, 0, 0);
366 
367       /* XXX: what if mmap() has failed! */
368       VGLDisplay->Type = VIDBUF8;	/* XXX */
369       switch (VGLModeInfo.vi_mem_model) {
370       case V_INFO_MM_PLANAR:
371 	if (VGLModeInfo.vi_depth == 4 && VGLModeInfo.vi_planes == 4) {
372 	  if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
373 	    VGLDisplay->Type = VIDBUF4S;
374 	  else
375 	    VGLDisplay->Type = VIDBUF4;
376 	} else {
377 	  /* shouldn't be happening */
378 	}
379         break;
380       case V_INFO_MM_PACKED:
381 	if (VGLModeInfo.vi_depth == 8) {
382 	  if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
383 	    VGLDisplay->Type = VIDBUF8S;
384 	  else
385 	    VGLDisplay->Type = VIDBUF8;
386 	}
387         break;
388       case V_INFO_MM_VGAX:
389 	VGLDisplay->Type = VIDBUF8X;
390 	break;
391       case V_INFO_MM_DIRECT:
392 	switch (VGLModeInfo.vi_pixel_size) {
393 	  case 2:
394 	    if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
395 	      VGLDisplay->Type = VIDBUF16S;
396 	    else
397 	      VGLDisplay->Type = VIDBUF16;
398 	    break;
399 	  case 3:
400 	    if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
401 	      VGLDisplay->Type = VIDBUF24S;
402 	    else
403 	      VGLDisplay->Type = VIDBUF24;
404 	    break;
405 	  case 4:
406 	    if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
407 	      VGLDisplay->Type = VIDBUF32S;
408 	    else
409 	      VGLDisplay->Type = VIDBUF32;
410 	    break;
411 	  default:
412 	  /* shouldn't be happening */
413           break;
414         }
415       default:
416 	/* shouldn't be happening */
417         break;
418       }
419 
420       VGLDisplay->Bitmap = VGLMem;
421       VGLDisplay->Xsize = VGLModeInfo.vi_width;
422       VGLDisplay->Ysize = VGLModeInfo.vi_height;
423       VGLSetVScreenSize(VGLDisplay, VGLDisplay->VXsize, VGLDisplay->VYsize);
424       VGLPanScreen(VGLDisplay, VGLDisplay->Xorigin, VGLDisplay->Yorigin);
425       switch (VGLDisplay->Type) {
426       case VIDBUF4S:
427 	outb(0x3c6, 0xff);
428 	outb(0x3ce, 0x01); outb(0x3cf, 0x00);		/* set/reset enable */
429 	outb(0x3ce, 0x08); outb(0x3cf, 0xff);		/* bit mask */
430 	for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
431 	     offset += len) {
432 	  VGLSetSegment(offset);
433 	  len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
434 		    VGLAdpInfo.va_window_size);
435 	  for (i = 0; i < VGLModeInfo.vi_planes; i++) {
436 	    outb(0x3c4, 0x02);
437 	    outb(0x3c5, 0x01<<i);
438 	    bcopy(&VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
439 		  VGLMem, len);
440 	  }
441 	}
442 	break;
443       case VIDBUF4:
444       case VIDBUF8X:
445 	outb(0x3c6, 0xff);
446 	outb(0x3ce, 0x01); outb(0x3cf, 0x00);		/* set/reset enable */
447 	outb(0x3ce, 0x08); outb(0x3cf, 0xff);		/* bit mask */
448 	for (i = 0; i < VGLModeInfo.vi_planes; i++) {
449 	  outb(0x3c4, 0x02);
450 	  outb(0x3c5, 0x01<<i);
451 	  bcopy(&VGLBuf[i*VGLAdpInfo.va_window_size], VGLMem,
452 		VGLAdpInfo.va_window_size);
453 	}
454 	break;
455       case VIDBUF8:
456       case VIDBUF8S:
457       case VIDBUF16:
458       case VIDBUF16S:
459       case VIDBUF24:
460       case VIDBUF24S:
461       case VIDBUF32:
462       case VIDBUF32S:
463 	for (offset = 0; offset < VGLBufSize; offset += len) {
464 	  VGLSetSegment(offset);
465 	  len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
466           bcopy(&VGLBuf[offset], VGLMem, len);
467 	}
468 	break;
469       }
470       VGLRestorePalette();
471       ioctl(0, VT_RELDISP, VT_ACKACQ);
472     }
473     else {
474       switch (VGLDisplay->Type) {
475       case VIDBUF4S:
476 	for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
477 	     offset += len) {
478 	  VGLSetSegment(offset);
479 	  len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
480 		    VGLAdpInfo.va_window_size);
481 	  for (i = 0; i < VGLModeInfo.vi_planes; i++) {
482 	    outb(0x3ce, 0x04);
483 	    outb(0x3cf, i);
484 	    bcopy(VGLMem, &VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
485 		  len);
486 	  }
487 	}
488 	break;
489       case VIDBUF4:
490       case VIDBUF8X:
491 	/*
492 	 * NOTE: the saved buffer is NOT in the MEMBUF format which
493 	 * the ordinary memory bitmap object is stored in. XXX
494 	 */
495 	for (i = 0; i < VGLModeInfo.vi_planes; i++) {
496 	  outb(0x3ce, 0x04);
497 	  outb(0x3cf, i);
498 	  bcopy(VGLMem, &VGLBuf[i*VGLAdpInfo.va_window_size],
499 		VGLAdpInfo.va_window_size);
500 	}
501 	break;
502       case VIDBUF8:
503       case VIDBUF8S:
504       case VIDBUF16:
505       case VIDBUF16S:
506       case VIDBUF24:
507       case VIDBUF24S:
508       case VIDBUF32:
509       case VIDBUF32S:
510 	for (offset = 0; offset < VGLBufSize; offset += len) {
511 	  VGLSetSegment(offset);
512 	  len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
513           bcopy(VGLMem, &VGLBuf[offset], len);
514 	}
515 	break;
516       }
517       VGLMem = MAP_FAILED;
518       munmap(VGLDisplay->Bitmap, VGLAdpInfo.va_window_size);
519       ioctl(0, VGLOldMode, 0);
520       ioctl(0, KDSETMODE, KD_TEXT);
521       ioctl(0, KDDISABIO, 0);
522       ioctl(0, VT_RELDISP, VT_TRUE);
523       VGLDisplay->Bitmap = VGLBuf;
524       VGLDisplay->Type = MEMBUF;
525       VGLDisplay->Xsize = VGLDisplay->VXsize;
526       VGLDisplay->Ysize = VGLDisplay->VYsize;
527       while (!VGLOnDisplay) pause();
528     }
529   }
530 }
531 
532 int
533 VGLSetSegment(unsigned int offset)
534 {
535   if (offset/VGLAdpInfo.va_window_size != VGLCurWindow) {
536     ioctl(0, CONS_SETWINORG, offset);		/* FBIO_SETWINORG */
537     VGLCurWindow = offset/VGLAdpInfo.va_window_size;
538   }
539   return (offset%VGLAdpInfo.va_window_size);
540 }
541 
542 int
543 VGLSetVScreenSize(VGLBitmap *object, int VXsize, int VYsize)
544 {
545   int depth;
546 
547   if (VXsize < object->Xsize || VYsize < object->Ysize)
548     return -1;
549   if (object->Type == MEMBUF)
550     return -1;
551   if (ioctl(0, FBIO_SETLINEWIDTH, &VXsize))
552     return -1;
553   ioctl(0, CONS_ADPINFO, &VGLAdpInfo);	/* FBIO_ADPINFO */
554   depth = VGLModeInfo.vi_depth;
555   if (depth == 15)
556     depth = 16;
557   object->VXsize = VGLAdpInfo.va_line_width
558 			   *8/(depth/VGLModeInfo.vi_planes);
559   object->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
560   if (VYsize < object->VYsize)
561     object->VYsize = VYsize;
562 
563 #ifdef LIBVGL_DEBUG
564   fprintf(stderr, "new size: VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
565 	  object->Xsize, object->Ysize, object->VXsize, object->VYsize);
566 #endif
567 
568   return 0;
569 }
570 
571 int
572 VGLPanScreen(VGLBitmap *object, int x, int y)
573 {
574   video_display_start_t origin;
575 
576   if (x < 0 || x + object->Xsize > object->VXsize
577       || y < 0 || y + object->Ysize > object->VYsize)
578     return -1;
579   if (object->Type == MEMBUF)
580     return 0;
581   origin.x = x;
582   origin.y = y;
583   if (ioctl(0, FBIO_SETDISPSTART, &origin))
584     return -1;
585   object->Xorigin = x;
586   object->Yorigin = y;
587 
588 #ifdef LIBVGL_DEBUG
589   fprintf(stderr, "new origin: (%d, %d)\n", x, y);
590 #endif
591 
592   return 0;
593 }
594