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