main.c revision 66834
1264269Ssbruno/*-
2264269Ssbruno * Copyright (c) 1991-1997 S�ren Schmidt
3264269Ssbruno * All rights reserved.
4264269Ssbruno *
5264269Ssbruno * Redistribution and use in source and binary forms, with or without
6264269Ssbruno * modification, are permitted provided that the following conditions
7264269Ssbruno * are met:
8264269Ssbruno * 1. Redistributions of source code must retain the above copyright
9264269Ssbruno *    notice, this list of conditions and the following disclaimer
10264269Ssbruno *    in this position and unchanged.
11264269Ssbruno * 2. Redistributions in binary form must reproduce the above copyright
12264269Ssbruno *    notice, this list of conditions and the following disclaimer in the
13264269Ssbruno *    documentation and/or other materials provided with the distribution.
14264269Ssbruno * 3. The name of the author may not be used to endorse or promote products
15264269Ssbruno *    derived from this software withough specific prior written permission
16264269Ssbruno *
17264269Ssbruno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18264269Ssbruno * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19264269Ssbruno * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20264269Ssbruno * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21264269Ssbruno * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22264269Ssbruno * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23264269Ssbruno * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24264269Ssbruno * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25264269Ssbruno * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26264269Ssbruno * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27264269Ssbruno *
28264269Ssbruno * $FreeBSD: head/lib/libvgl/main.c 66834 2000-10-08 21:34:00Z phk $
29264269Ssbruno */
30277155Swblock
31277155Swblock#include <stdio.h>
32264269Ssbruno#include <sys/types.h>
33264269Ssbruno#include <sys/signal.h>
34264269Ssbruno#include <sys/file.h>
35264269Ssbruno#include <sys/ioctl.h>
36264269Ssbruno#include <sys/mman.h>
37264269Ssbruno#include <sys/fbio.h>
38264269Ssbruno#include <sys/kbio.h>
39264269Ssbruno#include <sys/consio.h>
40266272Ssbruno#include "vgl.h"
41264269Ssbruno
42266272Ssbruno#define min(x, y)	(((x) < (y)) ? (x) : (y))
43264269Ssbruno#define max(x, y)	(((x) > (y)) ? (x) : (y))
44264269Ssbruno
45264269SsbrunoVGLBitmap *VGLDisplay;
46266272Ssbrunovideo_info_t VGLModeInfo;
47266272Ssbrunovideo_adapter_info_t VGLAdpInfo;
48266272Ssbrunobyte *VGLBuf;
49264269Ssbruno
50264269Ssbrunostatic int VGLMode;
51264269Ssbrunostatic int VGLOldMode;
52264269Ssbrunostatic size_t VGLBufSize;
53264269Ssbrunostatic byte *VGLMem = MAP_FAILED;
54264269Ssbrunostatic int VGLSwitchPending;
55264269Ssbrunostatic int VGLOnDisplay;
56264269Ssbrunostatic unsigned int VGLCurWindow;
57264269Ssbrunostatic int VGLInitDone = 0;
58264269Ssbruno
59264269Ssbrunovoid
60264269SsbrunoVGLEnd()
61264269Ssbruno{
62264269Ssbrunostruct vt_mode smode;
63264269Ssbruno
64264269Ssbruno  if (!VGLInitDone)
65264269Ssbruno    return;
66264269Ssbruno  VGLInitDone = 0;
67264269Ssbruno
68266272Ssbruno  signal(SIGUSR1, SIG_IGN);
69266272Ssbruno
70266272Ssbruno  if (VGLMem != MAP_FAILED) {
71266272Ssbruno    VGLClear(VGLDisplay, 0);
72266272Ssbruno    munmap(VGLMem, VGLAdpInfo.va_window_size);
73264269Ssbruno  }
74264269Ssbruno
75264269Ssbruno  if (VGLOldMode >= M_VESA_BASE) {
76264269Ssbruno    /* ugly, but necessary */
77264269Ssbruno    ioctl(0, _IO('V', VGLOldMode - M_VESA_BASE), 0);
78264269Ssbruno    if (VGLOldMode == M_VESA_800x600) {
79264269Ssbruno      int size[3];
80264269Ssbruno      size[0] = 80;
81266272Ssbruno      size[1] = 25;
82264269Ssbruno      size[2] = 16;
83264269Ssbruno      ioctl(0, KDRASTER, size);
84264269Ssbruno    }
85264269Ssbruno  } else {
86264269Ssbruno    ioctl(0, _IO('S', VGLOldMode), 0);
87266272Ssbruno  }
88266272Ssbruno  ioctl(0, KDDISABIO, 0);
89266272Ssbruno  ioctl(0, KDSETMODE, KD_TEXT);
90264269Ssbruno  smode.mode = VT_AUTO;
91266272Ssbruno  ioctl(0, VT_SETMODE, &smode);
92266272Ssbruno  if (VGLBuf)
93264269Ssbruno    free(VGLBuf);
94264269Ssbruno  VGLBuf = NULL;
95266272Ssbruno  free(VGLDisplay);
96264269Ssbruno  VGLDisplay = NULL;
97264269Ssbruno  VGLKeyboardEnd();
98264269Ssbruno}
99266272Ssbruno
100264269Ssbrunostatic void
101264269SsbrunoVGLAbort()
102264269Ssbruno{
103264269Ssbruno  VGLEnd();
104264269Ssbruno  exit(0);
105264269Ssbruno}
106264269Ssbruno
107264269Ssbrunostatic void
108264269SsbrunoVGLSwitch()
109264269Ssbruno{
110266272Ssbruno  if (!VGLOnDisplay)
111264269Ssbruno    VGLOnDisplay = 1;
112264269Ssbruno  else
113264269Ssbruno    VGLOnDisplay = 0;
114264269Ssbruno  VGLSwitchPending = 1;
115266272Ssbruno  signal(SIGUSR1, VGLSwitch);
116266272Ssbruno}
117266272Ssbruno
118264269Ssbrunoint
119264269SsbrunoVGLInit(int mode)
120264269Ssbruno{
121266272Ssbruno  struct vt_mode smode;
122266272Ssbruno  int adptype;
123266272Ssbruno
124266272Ssbruno  if (VGLInitDone)
125264269Ssbruno    return -1;
126264269Ssbruno
127264269Ssbruno  signal(SIGUSR1, VGLSwitch);
128266272Ssbruno  signal(SIGINT, VGLAbort);
129264269Ssbruno  signal(SIGTERM, VGLAbort);
130266272Ssbruno  signal(SIGSEGV, VGLAbort);
131266272Ssbruno  signal(SIGBUS, VGLAbort);
132266272Ssbruno
133264269Ssbruno  VGLOnDisplay = 1;
134264269Ssbruno  VGLSwitchPending = 0;
135264269Ssbruno
136264269Ssbruno  if (ioctl(0, CONS_GET, &VGLOldMode) || ioctl(0, CONS_CURRENT, &adptype))
137264269Ssbruno    return -1;
138264269Ssbruno  if (IOCGROUP(mode) == 'V')	/* XXX: this is ugly */
139264269Ssbruno    VGLModeInfo.vi_mode = (mode & 0x0ff) + M_VESA_BASE;
140264269Ssbruno  else
141264269Ssbruno    VGLModeInfo.vi_mode = mode & 0x0ff;
142264269Ssbruno  if (ioctl(0, CONS_MODEINFO, &VGLModeInfo))	/* FBIO_MODEINFO */
143266272Ssbruno    return -1;
144264269Ssbruno
145277155Swblock  VGLDisplay = (VGLBitmap *)malloc(sizeof(VGLBitmap));
146264269Ssbruno  if (VGLDisplay == NULL)
147264269Ssbruno    return -2;
148264269Ssbruno
149277155Swblock  if (ioctl(0, KDENABIO, 0)) {
150277155Swblock    free(VGLDisplay);
151277155Swblock    return -3;
152277155Swblock  }
153277155Swblock
154277155Swblock  VGLInitDone = 1;
155264269Ssbruno
156264269Ssbruno  /*
157277155Swblock   * vi_mem_model specifies the memory model of the current video mode
158266272Ssbruno   * in -CURRENT.
159266272Ssbruno   */
160266272Ssbruno  switch (VGLModeInfo.vi_mem_model) {
161264269Ssbruno  case V_INFO_MM_PLANAR:
162264269Ssbruno    /* we can handle EGA/VGA planner modes only */
163277155Swblock    if (VGLModeInfo.vi_depth != 4 || VGLModeInfo.vi_planes != 4
164277155Swblock	|| (adptype != KD_EGA && adptype != KD_VGA)) {
165277155Swblock      VGLEnd();
166266272Ssbruno      return -4;
167264269Ssbruno    }
168277155Swblock    VGLDisplay->Type = VIDBUF4;
169264269Ssbruno    break;
170277155Swblock  case V_INFO_MM_PACKED:
171266272Ssbruno    /* we can do only 256 color packed modes */
172264269Ssbruno    if (VGLModeInfo.vi_depth != 8) {
173277155Swblock      VGLEnd();
174264269Ssbruno      return -4;
175277155Swblock    }
176266272Ssbruno    VGLDisplay->Type = VIDBUF8;
177264269Ssbruno    break;
178277155Swblock  case V_INFO_MM_VGAX:
179264269Ssbruno    VGLDisplay->Type = VIDBUF8X;
180277155Swblock    break;
181266272Ssbruno  default:
182264269Ssbruno    VGLEnd();
183277155Swblock    return -4;
184277155Swblock  }
185277155Swblock
186277155Swblock  ioctl(0, VT_WAITACTIVE, 0);
187277155Swblock  ioctl(0, KDSETMODE, KD_GRAPHICS);
188277155Swblock  if (ioctl(0, mode, 0)) {
189277155Swblock    VGLEnd();
190277155Swblock    return -5;
191277155Swblock  }
192277155Swblock  if (ioctl(0, CONS_ADPINFO, &VGLAdpInfo)) {	/* FBIO_ADPINFO */
193277155Swblock    VGLEnd();
194277155Swblock    return -6;
195277155Swblock  }
196277155Swblock
197277155Swblock  /*
198277155Swblock   * Calculate the shadow screen buffer size.  In -CURRENT, va_buffer_size
199277155Swblock   * always holds the entire frame buffer size, wheather it's in the linear
200277155Swblock   * mode or windowed mode.
201277155Swblock   *     VGLBufSize = VGLAdpInfo.va_buffer_size;
202277155Swblock   * In -STABLE, va_buffer_size holds the frame buffer size, only if
203277155Swblock   * the linear frame buffer mode is supported. Otherwise the field is zero.
204277155Swblock   * We shall calculate the minimal size in this case:
205277155Swblock   *     VGLAdpInfo.va_line_width*VGLModeInfo.vi_height*VGLModeInfo.vi_planes
206277155Swblock   * or
207277155Swblock   *     VGLAdpInfo.va_window_size*VGLModeInfo.vi_planes;
208277155Swblock   * Use whichever is larger.
209277155Swblock   */
210277155Swblock  if (VGLAdpInfo.va_buffer_size != 0)
211277155Swblock    VGLBufSize = VGLAdpInfo.va_buffer_size;
212277155Swblock  else
213277155Swblock    VGLBufSize = max(VGLAdpInfo.va_line_width*VGLModeInfo.vi_height,
214277155Swblock		     VGLAdpInfo.va_window_size)*VGLModeInfo.vi_planes;
215277155Swblock  VGLBuf = malloc(VGLBufSize);
216277155Swblock  if (VGLBuf == NULL) {
217277155Swblock    VGLEnd();
218277155Swblock    return -7;
219277155Swblock  }
220277155Swblock
221277155Swblock#ifdef LIBVGL_DEBUG
222277155Swblock  fprintf(stderr, "VGLBufSize:0x%x\n", VGLBufSize);
223277155Swblock#endif
224277155Swblock
225277155Swblock  /* see if we are in the windowed buffer mode or in the linear buffer mode */
226277155Swblock  if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) {
227277155Swblock    if (VGLDisplay->Type == VIDBUF4)
228277155Swblock      VGLDisplay->Type = VIDBUF4S;
229277155Swblock    else if (VGLDisplay->Type == VIDBUF8)
230277155Swblock      VGLDisplay->Type = VIDBUF8S;
231277155Swblock  }
232277155Swblock
233277155Swblock  VGLMode = mode;
234277155Swblock  VGLCurWindow = 0;
235277155Swblock
236277155Swblock  VGLDisplay->Xsize = VGLModeInfo.vi_width;
237277155Swblock  VGLDisplay->Ysize = VGLModeInfo.vi_height;
238277155Swblock  VGLDisplay->VXsize = VGLAdpInfo.va_line_width
239277155Swblock			   *8/(VGLModeInfo.vi_depth/VGLModeInfo.vi_planes);
240277155Swblock  VGLDisplay->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
241277155Swblock  VGLDisplay->Xorigin = 0;
242277155Swblock  VGLDisplay->Yorigin = 0;
243277155Swblock
244277155Swblock  VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
245277155Swblock		       MAP_FILE, 0, 0);
246277155Swblock  if (VGLMem == MAP_FAILED) {
247277155Swblock    VGLEnd();
248277155Swblock    return -7;
249277155Swblock  }
250277155Swblock  VGLDisplay->Bitmap = VGLMem;
251277155Swblock
252277155Swblock  VGLSavePalette();
253277155Swblock
254277155Swblock#ifdef LIBVGL_DEBUG
255277155Swblock  fprintf(stderr, "va_line_width:%d\n", VGLAdpInfo.va_line_width);
256277155Swblock  fprintf(stderr, "VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
257277155Swblock	  VGLDisplay->Xsize, VGLDisplay->Ysize,
258277155Swblock	  VGLDisplay->VXsize, VGLDisplay->VYsize);
259277155Swblock#endif
260277155Swblock
261277155Swblock  smode.mode = VT_PROCESS;
262277155Swblock  smode.waitv = 0;
263277155Swblock  smode.relsig = SIGUSR1;
264277155Swblock  smode.acqsig = SIGUSR1;
265277155Swblock  smode.frsig  = SIGINT;
266277155Swblock  if (ioctl(0, VT_SETMODE, &smode)) {
267277155Swblock    VGLEnd();
268277155Swblock    return -9;
269277155Swblock  }
270277155Swblock  VGLTextSetFontFile((byte*)0);
271277155Swblock  VGLClear(VGLDisplay, 0);
272277155Swblock  return 0;
273277155Swblock}
274277155Swblock
275277155Swblockvoid
276277155SwblockVGLCheckSwitch()
277277155Swblock{
278277155Swblock  while (VGLSwitchPending) {
279277155Swblock    unsigned int offset;
280277155Swblock    unsigned int len;
281277155Swblock    int i;
282277155Swblock
283277155Swblock    VGLSwitchPending = 0;
284277155Swblock    if (VGLOnDisplay) {
285277155Swblock      ioctl(0, KDENABIO, 0);
286277155Swblock      ioctl(0, KDSETMODE, KD_GRAPHICS);
287264269Ssbruno      ioctl(0, VGLMode, 0);
288266272Ssbruno      VGLCurWindow = 0;
289277155Swblock      VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
290277155Swblock			   MAP_FILE, 0, 0);
291264269Ssbruno
292264269Ssbruno      /* XXX: what if mmap() has failed! */
293264269Ssbruno      VGLDisplay->Type = VIDBUF8;	/* XXX */
294264269Ssbruno      switch (VGLModeInfo.vi_mem_model) {
295266272Ssbruno      case V_INFO_MM_PLANAR:
296264269Ssbruno	if (VGLModeInfo.vi_depth == 4 && VGLModeInfo.vi_planes == 4) {
297264269Ssbruno	  if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
298264269Ssbruno	    VGLDisplay->Type = VIDBUF4S;
299	  else
300	    VGLDisplay->Type = VIDBUF4;
301	} else {
302	  /* shouldn't be happening */
303	}
304        break;
305      case V_INFO_MM_PACKED:
306	if (VGLModeInfo.vi_depth == 8) {
307	  if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
308	    VGLDisplay->Type = VIDBUF8S;
309	  else
310	    VGLDisplay->Type = VIDBUF8;
311	} else {
312	  /* shouldn't be happening */
313	}
314        break;
315      case V_INFO_MM_VGAX:
316	VGLDisplay->Type = VIDBUF8X;
317	break;
318      default:
319	/* shouldn't be happening */
320        break;
321      }
322
323      VGLDisplay->Bitmap = VGLMem;
324      VGLDisplay->Xsize = VGLModeInfo.vi_width;
325      VGLDisplay->Ysize = VGLModeInfo.vi_height;
326      VGLSetVScreenSize(VGLDisplay, VGLDisplay->VXsize, VGLDisplay->VYsize);
327      VGLPanScreen(VGLDisplay, VGLDisplay->Xorigin, VGLDisplay->Yorigin);
328      switch (VGLDisplay->Type) {
329      case VIDBUF4S:
330	outb(0x3c6, 0xff);
331	outb(0x3ce, 0x01); outb(0x3cf, 0x00);		/* set/reset enable */
332	outb(0x3ce, 0x08); outb(0x3cf, 0xff);		/* bit mask */
333	for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
334	     offset += len) {
335	  VGLSetSegment(offset);
336	  len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
337		    VGLAdpInfo.va_window_size);
338	  for (i = 0; i < VGLModeInfo.vi_planes; i++) {
339	    outb(0x3c4, 0x02);
340	    outb(0x3c5, 0x01<<i);
341	    bcopy(&VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
342		  VGLMem, len);
343	  }
344	}
345	break;
346      case VIDBUF4:
347      case VIDBUF8X:
348	outb(0x3c6, 0xff);
349	outb(0x3ce, 0x01); outb(0x3cf, 0x00);		/* set/reset enable */
350	outb(0x3ce, 0x08); outb(0x3cf, 0xff);		/* bit mask */
351	for (i = 0; i < VGLModeInfo.vi_planes; i++) {
352	  outb(0x3c4, 0x02);
353	  outb(0x3c5, 0x01<<i);
354	  bcopy(&VGLBuf[i*VGLAdpInfo.va_window_size], VGLMem,
355		VGLAdpInfo.va_window_size);
356	}
357	break;
358      case VIDBUF8:
359      case VIDBUF8S:
360	for (offset = 0; offset < VGLBufSize; offset += len) {
361	  VGLSetSegment(offset);
362	  len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
363          bcopy(&VGLBuf[offset], VGLMem, len);
364	}
365	break;
366      }
367      VGLRestorePalette();
368      ioctl(0, VT_RELDISP, VT_ACKACQ);
369    }
370    else {
371      switch (VGLDisplay->Type) {
372      case VIDBUF4S:
373	for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes;
374	     offset += len) {
375	  VGLSetSegment(offset);
376	  len = min(VGLBufSize/VGLModeInfo.vi_planes - offset,
377		    VGLAdpInfo.va_window_size);
378	  for (i = 0; i < VGLModeInfo.vi_planes; i++) {
379	    outb(0x3ce, 0x04);
380	    outb(0x3cf, i);
381	    bcopy(VGLMem, &VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset],
382		  len);
383	  }
384	}
385	break;
386      case VIDBUF4:
387      case VIDBUF8X:
388	/*
389	 * NOTE: the saved buffer is NOT in the MEMBUF format which
390	 * the ordinary memory bitmap object is stored in. XXX
391	 */
392	for (i = 0; i < VGLModeInfo.vi_planes; i++) {
393	  outb(0x3ce, 0x04);
394	  outb(0x3cf, i);
395	  bcopy(VGLMem, &VGLBuf[i*VGLAdpInfo.va_window_size],
396		VGLAdpInfo.va_window_size);
397	}
398	break;
399      case VIDBUF8:
400      case VIDBUF8S:
401	for (offset = 0; offset < VGLBufSize; offset += len) {
402	  VGLSetSegment(offset);
403	  len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size);
404          bcopy(VGLMem, &VGLBuf[offset], len);
405	}
406	break;
407      }
408      VGLMem = MAP_FAILED;
409      munmap(VGLDisplay->Bitmap, VGLAdpInfo.va_window_size);
410      ioctl(0, VGLOldMode, 0);
411      ioctl(0, KDSETMODE, KD_TEXT);
412      ioctl(0, KDDISABIO, 0);
413      ioctl(0, VT_RELDISP, VT_TRUE);
414      VGLDisplay->Bitmap = VGLBuf;
415      VGLDisplay->Type = MEMBUF;
416      VGLDisplay->Xsize = VGLDisplay->VXsize;
417      VGLDisplay->Ysize = VGLDisplay->VYsize;
418      while (!VGLOnDisplay) pause();
419    }
420  }
421}
422
423int
424VGLSetSegment(unsigned int offset)
425{
426  if (offset/VGLAdpInfo.va_window_size != VGLCurWindow) {
427    ioctl(0, CONS_SETWINORG, offset);		/* FBIO_SETWINORG */
428    VGLCurWindow = offset/VGLAdpInfo.va_window_size;
429  }
430  return (offset%VGLAdpInfo.va_window_size);
431}
432
433int
434VGLSetVScreenSize(VGLBitmap *object, int VXsize, int VYsize)
435{
436  if (VXsize < object->Xsize || VYsize < object->Ysize)
437    return -1;
438  if (object->Type == MEMBUF)
439    return -1;
440  if (ioctl(0, FBIO_SETLINEWIDTH, &VXsize))
441    return -1;
442  ioctl(0, CONS_ADPINFO, &VGLAdpInfo);	/* FBIO_ADPINFO */
443  object->VXsize = VGLAdpInfo.va_line_width
444			   *8/(VGLModeInfo.vi_depth/VGLModeInfo.vi_planes);
445  object->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
446  if (VYsize < object->VYsize)
447    object->VYsize = VYsize;
448
449#ifdef LIBVGL_DEBUG
450  fprintf(stderr, "new size: VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
451	  object->Xsize, object->Ysize, object->VXsize, object->VYsize);
452#endif
453
454  return 0;
455}
456
457int
458VGLPanScreen(VGLBitmap *object, int x, int y)
459{
460  video_display_start_t origin;
461
462  if (x < 0 || x + object->Xsize > object->VXsize
463      || y < 0 || y + object->Ysize > object->VYsize)
464    return -1;
465  if (object->Type == MEMBUF)
466    return 0;
467  origin.x = x;
468  origin.y = y;
469  if (ioctl(0, FBIO_SETDISPSTART, &origin))
470    return -1;
471  object->Xorigin = x;
472  object->Yorigin = y;
473
474#ifdef LIBVGL_DEBUG
475  fprintf(stderr, "new origin: (%d, %d)\n", x, y);
476#endif
477
478  return 0;
479}
480