
/* $Id: sprite.q,v 1.9 2006/04/25 17:57:43 agraef Exp $
   sprite.q: This is a tiny test program demonstrating how to move an image
   with transparency over a background image. */

/* To use, evaluate `sprite', click left in the GGI window to show the sprite
   at the current pointer position, move the pointer to move it around, click
   left again to hide the sprite, etc. Press a key to terminate the input loop
   at any time. */

import ggi, magick;

/* Driver for the display visual. We use the DirectX target on Windows, and
   assume the X11 target everywhere else. */

target			= "directx" if pos "mingw" sysinfo >= 0;
			= "x" otherwise;

/* Globals, infrastructure code. */

def
  /* read in the background and the sprite image */
  BACK_IMG = read_image "logo:" (),
  SPRITE_IMG = read_image "mozilla.png" (),
  /* determine the dimensions of the background image and render it inside a
     GGI display visual */
  (W,H|_) = image_info BACK_IMG,
  VIS = ggi_open (sprintf "%s:-keepcursor" target),
  _ = ggi_set_mode VIS (sprintf "%dx%d.A8" (W,H)) ||
  /* make sure to set async mode here to reduce flickering */
  ggi_set_flags VIS GGI_FLAG_ASYNC ||
  /* also make sure that we only get absolute pointer movements */
  ggi_set_mask VIS
  (ggi_get_mask VIS and not GGI_EM_PTRRELATIVE or GGI_EM_PTRABSOLUTE) ||
  /* draw the background image */
  ggi_put_box VIS (0,0) (W,H) (get_image_pixels BACK_IMG (0,0) (W,H)) ||
  ggi_flush VIS,
  /* get the colorspace of the display visual */
  (_,_,_,_,_,_,_,_,D,S) =
  sscanf (ggi_get_mode VIS) "%dx%d.V%dx%d.F%d.D%dx%d.[%c%d/%d]",
  /* dimensions of the sprite image */
  (W,H|_) = image_info SPRITE_IMG,
  /* create a memory visual as back storage */
  BACK = ggi_open "memory",
  /* set the same colorspace as on the display visual, in order to avoid
     colorspace conversions (note that we need no alpha buffer here, since we
     only have to store the pixels of the background image) */
  _ = ggi_set_mode BACK (sprintf "%dx%d.[C%d/%d]" (W,H,D,S)),
  /* get the pixels of the sprite image */
  SPRITE = get_image_pixels SPRITE_IMG (0,0) (W,H);

/* Save background pixels in the BACK visual. */

save_back (X,Y)		= ggi_put_box BACK (0,0) (W,H)
			  (ggi_get_box VIS (X,Y) (W,H));

/* Restore background from the BACK visual. */

restore_back (X,Y)	= ggi_put_box VIS (X,Y) (W,H)
			  (ggi_get_box BACK (0,0) (W,H));

/* Draw the sprite. */

draw_sprite (X,Y)	= ggi_put_box VIS (X,Y) (W,H) SPRITE;

/* Show, hide and move the sprite on the display. */

show_sprite (X,Y)	= save_back (X,Y) ||
			  draw_sprite (X,Y) ||
			  ggi_flush VIS;

hide_sprite (X,Y)	= restore_back (X,Y) ||
			  ggi_flush VIS;

move_sprite (X,Y) (X1,Y1)
			= restore_back (X,Y) || save_back (X1,Y1) ||
			  draw_sprite (X1,Y1) ||
			  ggi_flush VIS
			    if (X,Y) <> (X1,Y1);
			= () otherwise;

/* The main function. */

sprite			= loop () ();

/* The input loop; take appropriate actions on mouse events. */

// use these offsets to center the sprite at the cursor
def W2 = W div 2, H2 = H div 2;

loop STATE POS		= process STATE POS (ggi_read VIS GGI_EM_ALL);

// exit when key pressed
process STATE _ (ggi_event TYPE _ _ _ _ _)
			= cleanup STATE
			    if TYPE = GGI_EV_KEYPRESS;

// clean up at exit
cleanup (X,Y)		= hide_sprite (X-W2,Y-H2);
cleanup _		= () otherwise;

// toggle sprite when left button pressed
process () (X,Y) (ggi_event TYPE _ _ _ _ PARAM)
			= show_sprite (X-W2,Y-H2) ||
			  loop (X,Y) ()
			    if (TYPE = GGI_EV_PTRBUTTONPRESS) and then
			       (PARAM = 1);
process (X,Y) _ (ggi_event TYPE _ _ _ _ PARAM)
			= hide_sprite (X-W2,Y-H2) ||
			  loop () (X,Y)
			    if (TYPE = GGI_EV_PTRBUTTONPRESS) and then
			       (PARAM = 1);

// move sprite when pointer is moved
process (X,Y) _ (ggi_event TYPE _ _ _ _ (X1,Y1|_))
			= move_sprite (X-W2,Y-H2) (X1-W2,Y1-H2) ||
			  loop (X1,Y1) ()
			    if TYPE = GGI_EV_PTRABSOLUTE;

// track the pointer position when the sprite is off
process () _ (ggi_event TYPE _ _ _ _ (X,Y|_))
			= loop () (X,Y)
			    if TYPE = GGI_EV_PTRABSOLUTE;

// default action: do nothing
process STATE POS _	= loop STATE POS otherwise;
