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