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