Exemplo n.º 1
0
//-----------------------------------------------------------------------------
// Capture_Screen(void)
// Capture current screen to a file
//-----------------------------------------------------------------------------
static void		Capture_Screen(void)
{
    //PALETTE     pal;
    BITMAP *    bmp;
    BITMAP *    source;
    char        s1[FILENAME_LEN];
    int         x_start, x_len;
    int         y_start, y_len;

    // Get a filename
    Capture_FileName_Get(s1);
    if (Capture.id_number >= CAPTURE_ID_MAX)
    {
        Msg(MSGT_USER, Msg_Get(MSG_Capture_Error_File));
        return;
    }

	if ((Meka_State == MEKA_STATE_FULLSCREEN) || (!g_Configuration.capture_include_gui)) 
	{
		// Fullscreen
		source = screenbuffer;
		x_start = cur_drv->x_start;
		y_start = cur_drv->y_show_start;
		x_len = cur_drv->x_res;
		y_len = cur_drv->y_res;

		// Crop left column
		if (g_Configuration.capture_crop_scrolling_column)
		{
			if ((cur_drv->id == DRV_SMS) && (tsms.VDP_VideoMode > 4) && (Mask_Left_8))
			{
				x_start += 8;
				x_len -= 8;
			}
		}

		// Automatic crop on tile boundaries (for map making)
		// In total, remove 8 pixels from each axis
		if (g_Configuration.capture_crop_align_8x8)
		{
			const int scroll_x = cur_machine.VDP.scroll_x_latched;
			const int scroll_y = cur_machine.VDP.scroll_y_latched;
			x_start += scroll_x & 7;
			y_start += 8 - (scroll_y & 7);
			x_len -= 8;
			y_len -= 8;
		}
	}
	else if (Meka_State == MEKA_STATE_GUI)
	{
		// GUI mode
		x_start = 0;
		y_start = 0;
		x_len = g_Configuration.video_mode_gui_res_x;
		y_len = g_Configuration.video_mode_gui_res_y;
		source = gui_buffer;
	}
	else
	{
		// Unknown Mode
		assert(0);
		return;
	}

    acquire_bitmap(source);
    bmp = create_sub_bitmap(source, x_start, y_start, x_len, y_len);
    if (bmp == NULL)
    {
        Msg(MSGT_USER, Msg_Get(MSG_Capture_Error));
        return;
    }
    release_bitmap(source);

    //get_palette(pal);
    if (save_bitmap(s1, bmp, NULL) != 0)
    {
        Msg(MSGT_USER, Msg_Get(MSG_Capture_Error));
        destroy_bitmap(bmp);
        return;
    }

    destroy_bitmap(bmp);

    // Verbose
    killpath(s1);
    Msg(MSGT_USER, Msg_Get(MSG_Capture_Done), s1);
}
Exemplo n.º 2
0
void triple_buffer(BITMAP **page) {
	int i = 0, w, repeat, active_page = 0;
	int time_diff, bar_number, *used_voices = NULL, hw_voices;
	int text_bar_height, half_bar_size;
	unsigned int radius;
	
	coord_t dot[2], dot_old[2], cal[2], cal_tmp[2];
	
	wiimote *wiimote;
	
	char *fonts[NUM_MAX_FONTS] = FONTS;
	FONT *font_msg, *font_notes;
	BITMAP *background;
	bars *bar;
	
	/* dot_old data initialization. dot_old's X coord is not used */
	dot_old[0].y = SCREEN_H;
	dot_old[1].y = SCREEN_H;
	
	/* Wiimote initialization*/
	wiimote = *wiiuse_init(1);
	
	/* Load fonts, size of font is related to SCREEN_W */
	do {
		font_msg = load_font(fonts[i], NULL, NULL);
		__ASSERT(font_msg, ERROR_CANT_LOAD_FONT(fonts[i]));
		w = text_length(font_msg, WIIMOTE_CONNECTION_MSG);
	} while (w >= SCREEN_W && ++i < (NUM_MAX_FONTS-1));
	
	/* Notes' font is smaller than messages' font */
	font_notes = load_font(fonts[i], NULL, NULL);
	__ASSERT(font_notes, ERROR_CANT_LOAD_FONT(fonts[i]));
	
	/* Initialize and calculate bars size, point radius, text bar height, and calculate half bar size for text output under bars */
	bar = bar_create(NUM_BARS);
	radius = RADIUS(bar);
	text_bar_height = TEXT_BAR_HEIGHT;
	half_bar_size = HALF_BAR_SIZE(bar);
	
	/* Control the max number of available hardware voices. Though ALSA driver voices are up to 64, max hardware voices generally are 8-16. Checking the allocated voices after the driver allocation seems to be the only way */
	hw_voices = get_mixer_voices();
	if (hw_voices < NUM_BARS)
		used_voices = reallocate_voices(bar, hw_voices);
	
	/* Install timer, 10 ticks a second */
	LOCK_VARIABLE(timer);
	LOCK_FUNCTION(inc_timer);
	install_int_ex(inc_timer, BPS_TO_TIMER(10));
	
	/* Load background from file */
	background = load_tga("images/back.tga", NULL);
	__ASSERT(background, ERROR_CANT_LOAD_IMAGE("back.tga"));
	
	/* Enables vertical syncronization*/
	vsync();
	
	i = 0;
	/* First frame, this lasts until wiimote is connected, the user is prompted to activate the wiimote by pressing 1 & 2 keys on the wiimote */
	while(i == 0 && !keypressed()) {
		active_page = start_credits3buf(page, active_page);
		
		clear_keybuf();
		
		title3buf(page, active_page, font_msg);
		
		clear_keybuf();
		
		repeat = 0;
		while(repeat++ < 8 && i == 0 && !keypressed()) {
			stretch_blit(background, page[active_page], 0, 0, background->w, background->h, 0, 0, SCREEN_W, SCREEN_H); // background
			if(repeat%2 == 1)
				textout_centre_ex(page[active_page], font_msg, WIIMOTE_CONNECTION_MSG, SCREEN_W/2, SCREEN_H/2, makecol(0, 0, 0), -1); // text prompt
			
			release_bitmap(page[active_page]);
			/* make sure the last flip request has actually happened */
			do {
			} while (poll_scroll());
			
			/* post a request to display the page we just drew */
			request_video_bitmap(page[active_page]);

			/* update counters to point to the next page */
			switch (active_page) {
				case 0: active_page = 1; break;
				case 1: active_page = 2; break;
				case 2: active_page = 0; break;
			}

			/* Search for a wiimote */
			i = wiiuse_find(&wiimote, 1, 1);
		}
	}
	
	/* Try to connect to the wiimote */
	__ASSERT(wiiuse_connect(&wiimote, 1) > 0, ERROR_CANT_OPEN_WIIMOTE);
	
	/* Activate the first led on the wiimote */
	wiiuse_set_leds(wiimote, WIIMOTE_LED_1);
	
	/* Activate the ir module on the wiimote */
	wiiuse_set_ir(wiimote, TRUE);
	
	wiiuse_motion_sensing(wiimote, FALSE);
	
	wiiuse_set_ir_sensitivity(wiimote, 1);
	wiiuse_set_flags(wiimote, WIIUSE_CONTINUOUS, 0);
	
	cal[0].x = 0;
	cal[0].y = 0;
	cal[1].x = 1023;
	cal[1].y = 767;
	
	/* CALIBRATION LOOP */
	for(repeat=0;repeat<2;repeat++) {
		clear_keybuf();
		
		while(!keypressed()) {
			if (wiiuse_poll(&wiimote, 1)) // if there are datas pending from/to wiimote or ESC is pressed
				if (key[KEY_ESC] || wiimote->event == WIIUSE_DISCONNECT || wiimote->event == WIIUSE_UNEXPECTED_DISCONNECT || IS_PRESSED(wiimote, WIIMOTE_BUTTON_HOME)) { // if ESC is pressed, if wiimote update fails, or if HOME key on wiimote is pressed
				wiiuse_disconnect(wiimote);
				destroy_bitmap(background);
				destroy_font(font_msg);
				destroy_font(font_notes);
				for (i=0;i<NUM_BARS;i++) {
					deallocate_voice(bar[i].voice);
					destroy_sample(bar[i].sound);
				}
				free(bar); // YO!! :-)
				free(used_voices);
				
				return;
				}
			/* background */
			stretch_blit(background, page[active_page], 0, 0, background->w, background->h, 0, 0, SCREEN_W, SCREEN_H);
			
			if (wiimote->ir.dot[0].visible) { // if ir source is visible
				/* Read coords from the wiimote's ir*/
				dot[0] = transpose(wiimote->ir.dot[0], cal, 0);
			}
			
			switch(repeat) {
				case 0:
					textout_centre_ex(page[active_page], font_msg, WIIMOTE_CAL_ASX, SCREEN_W/2, SCREEN_H/2, makecol(0, 0, 0), -1); // text prompt
					rect(page[active_page], dot[0].x, dot[0].y, SCREEN_W+1, SCREEN_H+1, makecol(0, 0, 0));
					break;
				case 1:
					textout_centre_ex(page[active_page], font_msg, WIIMOTE_CAL_BDX, SCREEN_W/2, SCREEN_H/2, makecol(0, 0, 0), -1); // text prompt
					
					rect(page[active_page], dot[1].x, dot[1].y, SCREEN_W+1, SCREEN_H+1, makecol(0, 0, 0));
					rect(page[active_page], -1, -1, dot[0].x, dot[0].y, makecol(0, 0, 0));
					break;
			}
			circlefill(page[active_page], dot[0].x, dot[0].y, radius, makecol(0, 0, 0));

			release_bitmap(page[active_page]);
			
			/* make sure the last flip request has actually happened */
			do {
			} while (poll_scroll());

			/* post a request to display the page we just drew */
			request_video_bitmap(page[active_page]);

			/* update counters to point to the next page */
			switch (active_page) {
				case 0: active_page = 1; break;
				case 1: active_page = 2; break;
				case 2: active_page = 0; break;
			}
		}
		
		cal_tmp[repeat].x = wiimote->ir.dot[0].x;
		cal_tmp[repeat].y = wiimote->ir.dot[0].y;
		dot[1] = dot[0];
	}
	
	__ASSERT((cal_tmp[0].x < cal_tmp[1].x && cal_tmp[0].y > cal_tmp[1].y), ERROR_WHILE_CALIBRATING);
	
	cal[0].x = cal_tmp[0].x;
	cal[0].y = 767-cal_tmp[0].y;
	cal[1].x = cal_tmp[1].x;
	cal[1].y = 767-cal_tmp[1].y;
	
	/* MAIN LOOP */
	while (TRUE) {
		/* Draw a frame */
		if (wiiuse_poll(&wiimote, 1) || key[KEY_ESC]) // if there are datas pending from/to wiimote or ESC is pressed
			if (key[KEY_ESC] || wiimote->event == WIIUSE_DISCONNECT || wiimote->event == WIIUSE_UNEXPECTED_DISCONNECT || IS_PRESSED(wiimote, WIIMOTE_BUTTON_HOME)) { // if ESC is pressed, if wiimote update fails, or if HOME key on wiimote is pressed
				wiiuse_disconnect(wiimote);
				destroy_bitmap(background);
				destroy_font(font_msg);
				destroy_font(font_notes);
				for (i=0;i<NUM_BARS;i++) {
					deallocate_voice(bar[i].voice);
					destroy_sample(bar[i].sound);
				}
				free(bar); // YO!! :-)
				free(used_voices);
				
				return;
			}
		
		/* background */
		stretch_blit(background, page[active_page], 0, 0, background->w, background->h, 0, 0, SCREEN_W, SCREEN_H);
		
		/* Xylophone's bars and notes names */
		for(i=0;i<NUM_BARS;i++) {
			if (bar[i].t_start != -1 && (time_diff = timer-bar[i].t_start) > TICKS_TO_BLACK) // if color animation ends
				bar[i].t_start = -1;
			if (bar[i].t_start == -1) // if no color animation
				bar[i].color = 0;
			else	bar[i].color = COLORVAL(time_diff); // if color animation is running
			
			/* Draw bar */
			rectfill(page[active_page], bar[i].min.x, bar[i].min.y, bar[i].max.x, bar[i].max.y, makecol(bar[i].color, bar[i].color, bar[i].color));
			/* Print bar's associated note */
			textout_centre_ex(page[active_page], font_notes, bar[i].note, (bar[i].min.x + half_bar_size), text_bar_height, makecol(0, 0, 0), -1);
		}
		
		// da normalizzare e da contenere nello schermo, ir
		for(i=0;i<MAX_IR_DOTS;i++) {
			if (wiimote->ir.dot[i].visible) { // if ir source is visible
				/* Read coords from the wiimote's ir*/
				dot[i] = transpose(wiimote->ir.dot[i], cal, radius);
				
				/* If the ir source is under the bars and in previous frame it was above the bars, then play the sound and start the animation */
				if (dot[i].y > bar[0].min.y-radius) {
					if(dot_old[i].y <= bar[0].min.y-radius) {
						/* This calculates on which bar the ir source actually is */
						bar_number = is_onbar(bar, dot[i].x, NUM_BARS);
						
						/* play bar_number's sound with specified volume */
						play_bar_voice(bar, bar_number, volume(dot[i].y-dot_old[i].y), used_voices, hw_voices);
					}
					
					/* The dot have not to go under the bars or out of the screen */
					circlefill(page[active_page], dot[i].x, bar[0].min.y-radius, radius, makecol(0, 0, 0));
				}
				else	circlefill(page[active_page], dot[i].x, dot[i].y, radius, makecol(0, 0, 0));
				
				dot_old[i].y = dot[i].y;
			}
			else dot_old[i].y = SCREEN_H;
		}

		release_bitmap(page[active_page]);

		/* make sure the last flip request has actually happened */
		do {
		} while (poll_scroll());

		/* post a request to display the page we just drew */
		request_video_bitmap(page[active_page]);

		/* update counters to point to the next page */
		switch (active_page) {
			case 0: active_page = 1; break;
			case 1: active_page = 2; break;
			case 2: active_page = 0; break;
		}
	}
}
Exemplo n.º 3
0
/* create_sub_bitmap:
 *  Creates a sub bitmap, ie. a bitmap sharing drawing memory with a
 *  pre-existing bitmap, but possibly with different clipping settings.
 *  Usually will be smaller, and positioned at some arbitrary point.
 *
 *  Mark Wodrich is the owner of the brain responsible this hugely useful 
 *  and beautiful function.
 */
BITMAP *create_sub_bitmap(BITMAP *parent, int x, int y, int width, int height)
{
   BITMAP *bitmap;
   int nr_pointers;
   int i;

   ASSERT(parent);
   ASSERT((x >= 0) && (y >= 0) && (x < parent->w) && (y < parent->h));
   ASSERT((width > 0) && (height > 0));
   ASSERT(system_driver);

   if (x+width > parent->w) 
      width = parent->w-x;

   if (y+height > parent->h) 
      height = parent->h-y;

   if (parent->vtable->create_sub_bitmap)
      return parent->vtable->create_sub_bitmap(parent, x, y, width, height);

   if (system_driver->create_sub_bitmap)
      return system_driver->create_sub_bitmap(parent, x, y, width, height);

   /* get memory for structure and line pointers */
   /* (see create_bitmap for the reason we need at least two) */
   nr_pointers = MAX(2, height);
   bitmap = _AL_MALLOC(sizeof(BITMAP) + (sizeof(char *) * nr_pointers));
   if (!bitmap)
      return NULL;

   acquire_bitmap(parent);

   bitmap->w = bitmap->cr = width;
   bitmap->h = bitmap->cb = height;
   bitmap->clip = TRUE;
   bitmap->cl = bitmap->ct = 0;
   bitmap->vtable = parent->vtable;
   bitmap->write_bank = parent->write_bank;
   bitmap->read_bank = parent->read_bank;
   bitmap->dat = NULL;
   bitmap->extra = NULL;
   bitmap->x_ofs = x + parent->x_ofs;
   bitmap->y_ofs = y + parent->y_ofs;
   bitmap->seg = parent->seg;

   /* All bitmaps are created with zero ID's. When a sub-bitmap is created,
    * a unique ID is needed to identify the relationship when blitting from
    * one to the other. This is obtained from the global variable
    * _sub_bitmap_id_count, which provides a sequence of integers (yes I
    * know it will wrap eventually, but not for a long time :-) If the
    * parent already has an ID the sub-bitmap adopts it, otherwise a new
    * ID is given to both the parent and the child.
    */
   if (!(parent->id & BMP_ID_MASK)) {
      parent->id |= _sub_bitmap_id_count;
      _sub_bitmap_id_count = (_sub_bitmap_id_count+1) & BMP_ID_MASK;
   }

   bitmap->id = parent->id | BMP_ID_SUB;
   bitmap->id &= ~BMP_ID_LOCKED;

   if (is_planar_bitmap(bitmap))
      x /= 4;

   x *= BYTES_PER_PIXEL(bitmap_color_depth(bitmap));

   /* setup line pointers: each line points to a line in the parent bitmap */
   for (i=0; i<height; i++)
      bitmap->line[i] = parent->line[y+i] + x;

   if (bitmap->vtable->set_clip)
      bitmap->vtable->set_clip(bitmap);

   if (parent->vtable->created_sub_bitmap)
      parent->vtable->created_sub_bitmap(bitmap, parent);

   if (system_driver->created_sub_bitmap)
      system_driver->created_sub_bitmap(bitmap, parent);

   if (parent->id & BMP_ID_VIDEO)
      _register_switch_bitmap(bitmap, parent);

   release_bitmap(parent);

   return bitmap;
}
Exemplo n.º 4
0
/* show_mouse:
 *  Tells Allegro to display a mouse pointer. This only works when the timer 
 *  module is active. The mouse pointer will be drawn onto the bitmap bmp, 
 *  which should normally be the hardware screen. To turn off the mouse 
 *  pointer, which you must do before you draw anything onto the screen, call 
 *  show_mouse(NULL). If you forget to turn off the mouse pointer when 
 *  drawing something, the SVGA bank switching code will become confused and 
 *  will produce garbage all over the screen.
 */
void show_mouse(BITMAP *bmp)
{
   if (!mouse_driver)
      return;

   remove_int(mouse_move);

   /* Remove the mouse cursor */
   if (_mouse_screen) {
      acquire_bitmap(_mouse_screen);

      if (gfx_capabilities & GFX_HW_CURSOR) {
	 gfx_driver->hide_mouse();
	 gfx_capabilities &= ~(GFX_HW_CURSOR|GFX_SYSTEM_CURSOR);
 	 hw_cursor_dirty = TRUE;
      }
      else
	 draw_mouse(TRUE, FALSE);

      release_bitmap(_mouse_screen);
   }

   _mouse_screen = bmp;

   if (bmp && (current_cursor != MOUSE_CURSOR_NONE)) {
      acquire_bitmap(_mouse_screen);

      /* Default system cursor? */
      if ((current_cursor != MOUSE_CURSOR_ALLEGRO) && allow_system_cursor) {
         if (mouse_driver && mouse_driver->select_system_cursor) {
            use_system_cursor = mouse_driver->select_system_cursor(current_cursor);
            if (use_system_cursor) {
               gfx_capabilities |= GFX_HW_CURSOR|GFX_SYSTEM_CURSOR;
               hw_cursor_dirty = FALSE;
               got_hw_cursor = TRUE;
            }
         }
      }
      else {
         use_system_cursor = FALSE;
      }

      /* Custom hardware cursor? */
      if (hw_cursor_dirty) {
	 got_hw_cursor = FALSE;

	 if ((gfx_driver) && (gfx_driver->set_mouse_sprite) && (!_dispsw_status))
	    if (gfx_driver->set_mouse_sprite(mouse_sprite, mouse_x_focus, mouse_y_focus) == 0)
	       got_hw_cursor = TRUE;

	 hw_cursor_dirty = FALSE;
      }
      
      /* Try to display hardware (custom or system) cursor */
      if ((got_hw_cursor) && (is_same_bitmap(bmp, screen)))
	 if (gfx_driver->show_mouse(bmp, mx=mouse_x, my=mouse_y) == 0)
	    gfx_capabilities |= GFX_HW_CURSOR;

      /* Draw cursor manually if we can't do that */
      if (!(gfx_capabilities & GFX_HW_CURSOR)) {
	 draw_mouse(FALSE, TRUE);
         use_system_cursor = FALSE;
      }

      release_bitmap(_mouse_screen);

      install_int(mouse_move, 10);
   }
   else {
      if (mouse_driver->timer_poll) 
	 install_int(mouse_move, 10);
   }
}
Exemplo n.º 5
0
/* spline:
 *  Draws a bezier spline onto the specified bitmap in the specified color.
 */
void _soft_spline(BITMAP *bmp, AL_CONST int points[8], int color)
{
   #define MAX_POINTS   64

   int xpts[MAX_POINTS], ypts[MAX_POINTS];
   int i;
   int num_points;
   int c;
   int old_drawing_mode, old_drawing_x_anchor, old_drawing_y_anchor;
   BITMAP *old_drawing_pattern;
   ASSERT(bmp);

   /* Calculate the number of points to draw. We want to draw as few as
      possible without loosing image quality. This algorithm is rather
      random; I have no motivation for it at all except that it seems to work
      quite well. The length of the spline is approximated by the sum of
      distances from first to second to third to last point. The number of
      points to draw is then the square root of this distance. I first tried
      to make the number of points proportional to this distance without
      taking the square root of it, but then short splines kind of had too
      few points and long splines had too many. Since sqrt() increases more
      for small input than for large, it seems in a way logical to use it,
      but I don't precisely have any mathematical proof for it. So if someone
      has a better idea of how this could be done, don't hesitate to let us
      know...
   */

   #undef DIST
   #define DIST(x, y) (sqrt((x) * (x) + (y) * (y)))
   num_points = (int)(sqrt(DIST(points[2]-points[0], points[3]-points[1]) +
                           DIST(points[4]-points[2], points[5]-points[3]) +
                           DIST(points[6]-points[4], points[7]-points[5])) *
                      1.2);

   if (num_points > MAX_POINTS)
      num_points = MAX_POINTS;

   calc_spline(points, num_points, xpts, ypts);

   acquire_bitmap(bmp);

   if ((_drawing_mode == DRAW_MODE_XOR) ||
       (_drawing_mode == DRAW_MODE_TRANS)) {
      /* Must compensate for the end pixel being drawn twice,
         hence the mess. */
      old_drawing_mode = _drawing_mode;
      old_drawing_pattern = _drawing_pattern;
      old_drawing_x_anchor = _drawing_x_anchor;
      old_drawing_y_anchor = _drawing_y_anchor;
      for (i=1; i<num_points-1; i++) {
         c = getpixel(bmp, xpts[i], ypts[i]);
         line(bmp, xpts[i-1], ypts[i-1], xpts[i], ypts[i], color);
         solid_mode();
         putpixel(bmp, xpts[i], ypts[i], c);
         drawing_mode(old_drawing_mode, old_drawing_pattern,
                      old_drawing_x_anchor, old_drawing_y_anchor);
      }
      line(bmp, xpts[i-1], ypts[i-1], xpts[i], ypts[i], color);
   }
   else {
      for (i=1; i<num_points; i++)
         line(bmp, xpts[i-1], ypts[i-1], xpts[i], ypts[i], color);
   }

   release_bitmap(bmp);
}
Exemplo n.º 6
0
void Alleg4Surface::unlock()
{
  ASSERT(m_lock > 0);
  if (--m_lock == 0)
    release_bitmap(m_bmp);
}
Exemplo n.º 7
0
int main(void)
{
   BITMAP *scroller;
   RGB black = { 0, 0, 0, 0 };
   int x = 0;
   int next_x;
   int h = 100;

   if (allegro_init() != 0)
      return 1;
   install_keyboard();

   if (set_gfx_mode(GFX_AUTODETECT, 320, 240, 640, 240) != 0) {
      set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
      allegro_message("Unable to set a 320x240 mode with 640x240 "
		      "virtual dimensions\n");
      return 1;
   }

   /* the scrolling area is twice the width of the screen (640x240) */
   scroller = create_sub_bitmap(screen, 0, 0, SCREEN_W*2, SCREEN_H);

   set_palette(desktop_palette);
   set_color(0, &black);

   rectfill(scroller, 0, 0, SCREEN_W, 100, 6);
   rectfill(scroller, 0, 100, SCREEN_W, SCREEN_H, 2);

   do {
      /* advance the scroller, wrapping every 320 pixels */
      next_x = x + 1;
      if (next_x >= 320)
	 next_x = 0;

      /* draw another column of the landscape */
      acquire_bitmap(scroller);
      vline(scroller, next_x+SCREEN_W-1, 0, h, 6);
      vline(scroller, next_x+SCREEN_W-1, h+1, SCREEN_H, 2);
      release_bitmap(scroller);

      /* scroll the screen */
      scroll_screen(next_x, 0);

      /* duplicate the landscape column so we can wrap the scroller */
      if (next_x > 0) {
	 acquire_bitmap(scroller);
	 vline(scroller, x, 0, h, 6);
	 vline(scroller, x, h+1, SCREEN_H, 2);
	 release_bitmap(scroller);
      }

      /* randomly alter the landscape position */
      if (AL_RAND()&1) {
	 if (h > 5)
	    h--;
      }
      else {
	 if (h < 195)
	    h++;
      }

      x = next_x;

      rest(0);
   } while (!keypressed());

   destroy_bitmap(scroller);

   clear_keybuf();

   return 0;
}