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