static void DISPMANX_DirectUpdate(_THIS, int numrects, SDL_Rect *rects)
{	
	//En OpenGL también va así­. No deberíamos esperar para cambiar el buffer, de hecho la aplicación
	//piensa que no hay doble buffer (hasta hemos desactivado el double buffer), sino que en lugar
	//de esperar, simplemente deberíamos actualizar la superficie visible directamente... Pero no
	//puedo hacer eso porque no tengo acceso a la superficie visible: tengo la superficie de vídeo en RAM
	//y cada vez que se hace un cambio (o sea, cada vez que llego aquí­) copio esa superficie a la VRAM
	//y espero a cambiar los buffers para no tener tearing, a pesar de que esta función se supone que no
	//hace eso. Pero en OpenGL se hace lo mismo ya que la única manera de mostrar los cambios es hacer
	//un GL_SWAP_BUFFERS que también es bloqueante. 
	//Volcamos desde el ram bitmap buffer al dispmanx resource buffer que toque. cada vez a uno.	
	vc_dispmanx_resource_write_data( dispvars->resources[flip_page], 
	   dispvars->pix_format, dispvars->pitch, dispvars->pixmem, 
	   &(dispvars->bmp_rect) );
	//Empieza actualización
	dispvars->update = vc_dispmanx_update_start( 0 );

	vc_dispmanx_element_change_source(dispvars->update, 
	   dispvars->element, dispvars->resources[flip_page]);
	
	vc_dispmanx_update_submit_sync( dispvars->update );		
	//vc_dispmanx_update_submit(dispvars->update, NULL, NULL); 
	//Acaba actualización
	flip_page = !flip_page;
	
	return;
}
示例#2
0
static void dispmanx_flip(struct dispmanx_page *page, void *data)
{
   struct dispmanx_video *_dispvars = data;

   if (!_dispvars)
      return;

   /* Dispmanx doesn't support issuing more than one pageflip. 
    * If we do, the second CB isn't called. */
   if (_dispvars->pageflip_pending > 0)
   {
      slock_lock(_dispvars->vsync_cond_mutex);
      scond_wait(_dispvars->vsync_condition, _dispvars->vsync_cond_mutex);
      slock_unlock(_dispvars->vsync_cond_mutex);
   }

   /* Issue a page flip at the next vblank interval 
    * (will be done at vsync anyway). */
   _dispvars->update = vc_dispmanx_update_start(0);

   vc_dispmanx_element_change_source(_dispvars->update, _dispvars->element,
         _dispvars->resources[page->numpage]);

   vc_dispmanx_update_submit(_dispvars->update, vsync_callback, (void*)page);

   slock_lock(_dispvars->pending_mutex);
   _dispvars->pageflip_pending++;	
   slock_unlock(_dispvars->pending_mutex);
}
static void dispmanx_surface_update(void *data, const void *frame, struct dispmanx_surface *surface)
{
   struct dispmanx_video *_dispvars = data;
   struct dispmanx_page *page = NULL;
   
   /* Wait until last issued flip completes to get a free page. Also, 
      dispmanx doesn't support issuing more than one pageflip.*/
   slock_lock(_dispvars->pending_mutex);
   if (_dispvars->pageflip_pending > 0)
   {
      scond_wait(_dispvars->vsync_condition, _dispvars->pending_mutex);
   }
   slock_unlock(_dispvars->pending_mutex);
  
   page = dispmanx_get_free_page(_dispvars, surface);
 
   /* Frame blitting */
   vc_dispmanx_resource_write_data(page->resource, surface->pixformat,
      surface->pitch, (void*)frame, &(surface->bmp_rect));
   
   /* Issue a page flip that will be done at the next vsync. */
   _dispvars->update = vc_dispmanx_update_start(0);

   vc_dispmanx_element_change_source(_dispvars->update, surface->element,
         page->resource);

   vc_dispmanx_update_submit(_dispvars->update, dispmanx_vsync_callback, (void*)page);

   slock_lock(_dispvars->pending_mutex);
   _dispvars->pageflip_pending++;	
   slock_unlock(_dispvars->pending_mutex);
}
static void fbDispmanSetNativeCursor(jlong nativeCursorHandle) {

    DISPMANX_UPDATE_HANDLE_T update;
    DispmanCursorImage *cursorImage = (DispmanCursorImage *)jlong_to_ptr(nativeCursorHandle);

    if (cursorImage != NULL && cursor.element != 0) {

        if (cursorImage->width != cursor.cursorWidth ||
                cursorImage->height != cursor.cursorHeight) {

            fbDispmanRemoveDispmanxElement();

            cursor.cursorWidth = cursorImage->width;
            cursor.cursorHeight = cursorImage->height;

            fbDispmanAddDispmanxElement();
        }
        cursor.currentCursor = nativeCursorHandle;

        if (cursor.isVisible) {
            update = vc_dispmanx_update_start(0);
            vc_dispmanx_element_change_source(update, cursor.element, cursorImage->resource);
            vc_dispmanx_update_submit_sync(update);
        }

    }
}
示例#5
0
void vsync(DISPMANX_UPDATE_HANDLE_T u, void* arg)
{

    int ret;
    DISPMANX_UPDATE_HANDLE_T    update;

    update = vc_dispmanx_update_start( 10 );
    assert( update );
    ret = vc_dispmanx_element_change_source( update, element, resource[next_resource]);
    assert( ret == 0 );
    ret = vc_dispmanx_update_submit_sync( update );
    assert( ret == 0 );

    if(next_resource != 2) {

        int real_next_resource = next_resource ^ 1;
        next_resource = 2; // use filler if next callback called before this one ends

        // fill image
        int n;
        for (n=0; n<HEIGHT; n+=2) {
            get_packet(ROW(n)+24); // +24 because clock never changes
            memcpy(ROW(n+1)+24, ROW(n)+24, 336); // double it up because the hardware scaler
                                                 // will introduce blurring between lines
        }

        // write to resource
        ret = vc_dispmanx_resource_write_data(  resource[real_next_resource], TYPE, PITCH, image, &image_rect );
        assert( ret == 0 );

        next_resource = real_next_resource; // queue up next real resource

    }
}
static int DISPMANX_FlipHWSurface(_THIS, SDL_Surface *surface)
{
	/*if ( switched_away ) {
		return -2; // no hardware access
	}*/
	
	//Volcamos desde el ram bitmap buffer al dispmanx resource buffer
	//que toque. cada vez a uno.	
	vc_dispmanx_resource_write_data( dispvars->resources[flip_page], 
	   dispvars->pix_format, dispvars->pitch, dispvars->pixmem, 
	   &(dispvars->bmp_rect) );

	//**Empieza actualización***
	dispvars->update = vc_dispmanx_update_start( 0 );

	vc_dispmanx_element_change_source(dispvars->update, 
	   dispvars->element, dispvars->resources[flip_page]);

	vc_dispmanx_update_submit_sync( dispvars->update );		
	//vc_dispmanx_update_submit(dispvars->update, NULL, NULL); 
	//**Acaba actualización***
	flip_page = !flip_page;
	
	//MAC Esto no hace falta porque SDL siempre escribe en dispvars->pixmem, 
	//que es el buffer en RAM y que copiamos cada vez a un resource.
	//surface->pixels = flip_address[flip_page];
	
	return (0);
}
示例#7
0
文件: vout.c 项目: Kubink/vlc
static void dmx_region_update(struct dmx_region_t *dmx_region,
                DISPMANX_UPDATE_HANDLE_T update, picture_t *picture)
{
    vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
                    picture->p[0].i_pitch, picture->p[0].p_pixels, &dmx_region->bmp_rect);
    vc_dispmanx_element_change_source(update, dmx_region->element, dmx_region->resource);
    dmx_region->picture = picture;
}
static void fbDispmanSetVisible(jboolean isVisible) {

    if (isVisible) {
        if (!cursor.isVisible && cursor.currentCursor != 0) {
            fbDispmanSetNativeCursor(cursor.currentCursor);
            DispmanCursorImage *cursorImage = 
                (DispmanCursorImage *)jlong_to_ptr(cursor.currentCursor);
            DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
            vc_dispmanx_element_change_source(update, cursor.element, 
                                              cursorImage->resource);
            vc_dispmanx_update_submit_sync(update);
        }
    } else {
        DISPMANX_UPDATE_HANDLE_T update;
        update = vc_dispmanx_update_start(0);
        vc_dispmanx_element_change_source(update, cursor.element, 0 );
        vc_dispmanx_update_submit_sync(update);
    }
    cursor.isVisible = isVisible;
}
示例#9
0
void
changeSourceWorms(
    WORMS_T *worms,
    DISPMANX_UPDATE_HANDLE_T update)
{
    int result = 0;
    result = vc_dispmanx_element_change_source(update,
                                               worms->element,
                                               worms->backResource);
    assert(result == 0);

    DISPMANX_RESOURCE_HANDLE_T tmp = worms->frontResource;
    worms->frontResource = worms->backResource;
    worms->backResource = tmp;
}
示例#10
0
void FE_DisplayScreen(void)
{
	VC_RECT_T dst_rect;

	vc_dispmanx_rect_set( &dst_rect, 0, 0, surface_width, surface_height );

	vc_dispmanx_resource_write_data( cur_res, VC_IMAGE_RGB565, surface_width*2, gp2x_screen15, &dst_rect );
	dispman_update = vc_dispmanx_update_start( 0 );
	vc_dispmanx_element_change_source( dispman_update, dispman_element, cur_res );
	vc_dispmanx_update_submit( dispman_update, 0, 0 );

	// swap current resource
	tmp_res = cur_res;
	cur_res = prev_res;
	prev_res = tmp_res;
}
示例#11
0
void
changeSourceLife(
    LIFE_T *life,
    DISPMANX_UPDATE_HANDLE_T update)
{
    int result = 0;
    result = vc_dispmanx_element_change_source(update,
                                               life->element,
                                               life->backResource);
    assert(result == 0);

    // swap front and back resources

    DISPMANX_RESOURCE_HANDLE_T tmp = life->frontResource;
    life->frontResource = life->backResource;
    life->backResource = tmp;
}
示例#12
0
static bool dispmanx_gfx_frame(void *data, const void *frame, unsigned width,
      unsigned height, unsigned pitch, const char *msg)
{
   struct dispmanx_video *_dispvars = data;

   /* Check if neither menu nor core framebuffer is to be displayed. */
   if (!_dispvars->menu_active && !frame)
      return true;

   if (width != _dispvars->width || height != _dispvars->height)
   {
      /* Sanity check. */
      if (width == 0 || height == 0)
         return true;

      RARCH_LOG("video_dispmanx: internal frame resolution changed by core\n");

      if (!dispmanx_setup_scale(_dispvars, width, height, pitch))
      {
         RARCH_ERR("video_dispmanx: frame resolution set failed\n");
         return false;
      }
      dispmanx_blank_console (_dispvars);
   }

   if (_dispvars->menu_active)
   {
      char buf[128];
      video_monitor_get_fps(buf, sizeof(buf), NULL, 0);

      /* Synchronous flipping of the menu buffers. */
      _dispvars->update = vc_dispmanx_update_start(0);
      vc_dispmanx_element_change_source(_dispvars->update, _dispvars->menu_element,
            _dispvars->menu_resources[_dispvars->menu_flip_page]);
      vc_dispmanx_update_submit_sync(_dispvars->update);		
      return true;
   }

   /* Update main game screen: locate free page, blit and flip. */
   dispmanx_update_main(_dispvars, frame);

   return true;
}
static void fbDispmanReleaseNativeCursor(jlong nativeCursorHandle) {

    DispmanCursorImage *cursorImage = (DispmanCursorImage *)jlong_to_ptr(nativeCursorHandle);

    if (cursorImage != NULL && cursorImage->resource != 0) {
        if (cursor.currentCursor == nativeCursorHandle && cursor.isVisible) {
            DISPMANX_UPDATE_HANDLE_T update;
            update = vc_dispmanx_update_start(0);
            vc_dispmanx_element_change_source(update, 
                                              cursor.element, 0 /* resource*/);
            vc_dispmanx_update_submit_sync(update);
        }
        vc_dispmanx_resource_delete(cursorImage->resource);
    }

    free(cursorImage);
    if (cursor.currentCursor == nativeCursorHandle) {
        cursor.currentCursor = 0;
    }
}
示例#14
0
static void *display_thread (void *unused)
{
	VC_DISPMANX_ALPHA_T alpha = {
		(DISPMANX_FLAGS_ALPHA_T)(DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS), 
		255 /*alpha 0->255*/ , 	0
	};
	uint32_t vc_image_ptr;
	SDL_Surface *Dummy_prSDLScreen;
	int width, height;
	bool callback_registered = false;
	
  for(;;) {
    display_thread_busy = false;
    uae_u32 signal = read_comm_pipe_u32_blocking(display_pipe);
    display_thread_busy = true;
    switch(signal) {
      case DISPLAY_SIGNAL_SETUP:
        if(!callback_registered) {
  				bcm_host_init();
  				dispmanxdisplay = vc_dispmanx_display_open(0);
  			  vc_dispmanx_vsync_callback(dispmanxdisplay, vsync_callback, NULL);
  			  callback_registered = true;
  			}
			  break;

			case DISPLAY_SIGNAL_SUBSHUTDOWN:
				if (DispManXElementpresent == 1) {
					DispManXElementpresent = 0;
					dispmanxupdate = vc_dispmanx_update_start(0);
					vc_dispmanx_element_remove(dispmanxupdate, dispmanxelement);
					vc_dispmanx_update_submit_sync(dispmanxupdate);
				}
			
				if (dispmanxresource_amigafb_1 != 0) {
					vc_dispmanx_resource_delete(dispmanxresource_amigafb_1);
					dispmanxresource_amigafb_1 = 0;
			  }
				if (dispmanxresource_amigafb_2 != 0) {
					vc_dispmanx_resource_delete(dispmanxresource_amigafb_2);
					dispmanxresource_amigafb_2 = 0;
			  }
			  
			  if(prSDLScreen != NULL) {
			    SDL_FreeSurface(prSDLScreen);
			    prSDLScreen = NULL;
			  }
        uae_sem_post (&display_sem);
        break;
				
			case DISPLAY_SIGNAL_OPEN:
				width = display_width;
				height = display_height;
				
				Dummy_prSDLScreen = SDL_SetVideoMode(width, height, 16, SDL_SWSURFACE | SDL_FULLSCREEN);
				prSDLScreen = SDL_CreateRGBSurface(SDL_HWSURFACE, width, height, 16,
					Dummy_prSDLScreen->format->Rmask,	Dummy_prSDLScreen->format->Gmask, Dummy_prSDLScreen->format->Bmask, Dummy_prSDLScreen->format->Amask);
				SDL_FreeSurface(Dummy_prSDLScreen);
			
				vc_dispmanx_display_get_info(dispmanxdisplay, &dispmanxdinfo);
			
				dispmanxresource_amigafb_1 = vc_dispmanx_resource_create(VC_IMAGE_RGB565, width, height, &vc_image_ptr);
				dispmanxresource_amigafb_2 = vc_dispmanx_resource_create(VC_IMAGE_RGB565, width, height, &vc_image_ptr);
			
				vc_dispmanx_rect_set(&blit_rect, 0, 0, width, height);
				vc_dispmanx_resource_write_data(dispmanxresource_amigafb_1, VC_IMAGE_RGB565, prSDLScreen->pitch, prSDLScreen->pixels, &blit_rect);
				vc_dispmanx_rect_set(&src_rect, 0, 0, width << 16, height << 16);
			
				// 16/9 to 4/3 ratio adaptation.
				if (currprefs.gfx_correct_aspect == 0 || screen_is_picasso) {
				  // Fullscreen.
					int scaled_width = dispmanxdinfo.width * currprefs.gfx_fullscreen_ratio / 100;
					int scaled_height = dispmanxdinfo.height * currprefs.gfx_fullscreen_ratio / 100;
					vc_dispmanx_rect_set( &dst_rect, (dispmanxdinfo.width - scaled_width)/2, (dispmanxdinfo.height - scaled_height)/2,
						scaled_width,	scaled_height);
				}	else {
				  // 4/3 shrink.
					int scaled_width = dispmanxdinfo.width * currprefs.gfx_fullscreen_ratio / 100;
					int scaled_height = dispmanxdinfo.height * currprefs.gfx_fullscreen_ratio / 100;
					vc_dispmanx_rect_set( &dst_rect, (dispmanxdinfo.width - scaled_width / 16 * 12)/2, (dispmanxdinfo.height - scaled_height)/2,
						scaled_width/16*12,	scaled_height);
				}
			
				if (DispManXElementpresent == 0) {
					DispManXElementpresent = 1;
					dispmanxupdate = vc_dispmanx_update_start(0);
					dispmanxelement = vc_dispmanx_element_add(dispmanxupdate, dispmanxdisplay,	2,               // layer
						&dst_rect, dispmanxresource_amigafb_1, &src_rect,	DISPMANX_PROTECTION_NONE,	&alpha,	NULL, DISPMANX_NO_ROTATE);
			
					vc_dispmanx_update_submit(dispmanxupdate, NULL, NULL);
				}
        uae_sem_post (&display_sem);
        break;

			case DISPLAY_SIGNAL_SHOW:
				if (current_resource_amigafb == 1) {
					current_resource_amigafb = 0;
				  vc_dispmanx_resource_write_data(dispmanxresource_amigafb_1, VC_IMAGE_RGB565,
					  adisplays.gfxvidinfo.drawbuffer.rowbytes, adisplays.gfxvidinfo.drawbuffer.bufmem, &blit_rect);
				  dispmanxupdate = vc_dispmanx_update_start(0);
				  vc_dispmanx_element_change_source(dispmanxupdate, dispmanxelement, dispmanxresource_amigafb_1);
				} else {
					current_resource_amigafb = 1;
					vc_dispmanx_resource_write_data(dispmanxresource_amigafb_2,	VC_IMAGE_RGB565,
						adisplays.gfxvidinfo.drawbuffer.rowbytes,	adisplays.gfxvidinfo.drawbuffer.bufmem,	&blit_rect);
					dispmanxupdate = vc_dispmanx_update_start(0);
					vc_dispmanx_element_change_source(dispmanxupdate, dispmanxelement, dispmanxresource_amigafb_2);
				}
			  vc_dispmanx_update_submit(dispmanxupdate, NULL, NULL);
				break;
								
      case DISPLAY_SIGNAL_QUIT:
        callback_registered = false;
			  vc_dispmanx_vsync_callback(dispmanxdisplay, NULL, NULL);
				vc_dispmanx_display_close(dispmanxdisplay);
				bcm_host_deinit();
				SDL_VideoQuit();
        display_tid = 0;
        return 0;
    }
  }
}
示例#15
0
void flush_screen ()
{
    //SDL_UnlockSurface (prSDLScreen);

    if (show_inputmode)
    {
        inputmode_redraw();	
    }


    if (savestate_state == STATE_DOSAVE)
    {
    if(delay_savestate_frame > 0)
      --delay_savestate_frame;
    else
    {
        CreateScreenshot();
        save_thumb(screenshot_filename);
        savestate_state = 0;
    }
  }

  unsigned long start = read_processor_time();
  //if(start < next_synctime && next_synctime - start > time_per_frame - 1000)
  //  usleep((next_synctime - start) - 1000);
  //SDL_Flip(prSDLScreen);


	if (current_resource_amigafb == 1)
	{
		current_resource_amigafb = 0;
		vc_dispmanx_resource_write_data(  dispmanxresource_amigafb_1,
	                                    VC_IMAGE_RGB565,
	                                    gfxvidinfo.width * 2,
	                                    gfxvidinfo.bufmem,
	                                    &blit_rect );
		dispmanxupdate = vc_dispmanx_update_start( 10 );
		vc_dispmanx_element_change_source(dispmanxupdate,dispmanxelement,dispmanxresource_amigafb_1);

		vc_dispmanx_update_submit(dispmanxupdate,vsync_callback,NULL);
		//vc_dispmanx_update_submit_sync(dispmanxupdate);

	}
	else
	{
		current_resource_amigafb = 1;
		vc_dispmanx_resource_write_data(  dispmanxresource_amigafb_2,
	                                    VC_IMAGE_RGB565,
	                                    gfxvidinfo.width * 2,
	                                    gfxvidinfo.bufmem,
	                                    &blit_rect );
		dispmanxupdate = vc_dispmanx_update_start( 10 );
		vc_dispmanx_element_change_source(dispmanxupdate,dispmanxelement,dispmanxresource_amigafb_2);

		vc_dispmanx_update_submit(dispmanxupdate,vsync_callback,NULL);
	}

  last_synctime = read_processor_time();
  
  if(last_synctime - next_synctime > time_per_frame - 1000)
    adjust_idletime(0);
  else
    adjust_idletime(next_synctime - start);
  
  if(last_synctime - next_synctime > time_per_frame - 5000)
    next_synctime = last_synctime + time_per_frame * (1 + currprefs.gfx_framerate);
  else
    next_synctime = next_synctime + time_per_frame * (1 + currprefs.gfx_framerate);

	init_row_map();

}
int
main(
    int argc,
    char *argv[])
{
    const char *program = basename(argv[0]);

    int fps = DEFAULT_FPS;
    suseconds_t frameDuration =  1000000 / fps;
    bool isDaemon =  false;
    uint32_t sourceDisplayNumber = DEFAULT_SOURCE_DISPLAY_NUMBER;
    uint32_t destDisplayNumber = DEFAULT_DESTINATION_DISPLAY_NUMBER;
	int32_t layerNumber = DEFAULT_LAYER_NUMBER;
    const char *pidfile = NULL;

    //---------------------------------------------------------------------

    static const char *sopts = "d:f:hl:p:s:D";
    static struct option lopts[] = 
    {
        { "destination", required_argument, NULL, 'd' },
        { "fps", required_argument, NULL, 'f' },
        { "help", no_argument, NULL, 'h' },
        { "layer", required_argument, NULL, 'l' },
        { "pidfile", required_argument, NULL, 'p' },
        { "source", required_argument, NULL, 's' },
        { "daemon", no_argument, NULL, 'D' },
        { NULL, no_argument, NULL, 0 }
    };

    int opt = 0;

    while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1)
    {
        switch (opt)
        {
        case 'd':

            destDisplayNumber = atoi(optarg);
            break;

        case 'f':

            fps = atoi(optarg);

            if (fps > 0)
            {
                frameDuration = 1000000 / fps;
            }
            else
            {
                fps = 1000000 / frameDuration;
            }

            break;

        case 'h':

            printUsage(stdout, program);
            exit(EXIT_SUCCESS);

            break;

        case 'l':

            layerNumber = atoi(optarg);
            break;

        case 'p':

            pidfile = optarg;

            break;

        case 's':

            sourceDisplayNumber = atoi(optarg);
            break;

        case 'D':

            isDaemon = true;
            break;

        default:

            printUsage(stderr, program);
            exit(EXIT_FAILURE);

            break;
        }
    }

    //---------------------------------------------------------------------

    struct pidfh *pfh = NULL;

    if (isDaemon)
    {
        if (pidfile != NULL)
        {
            pid_t otherpid;
            pfh = pidfile_open(pidfile, 0600, &otherpid);

            if (pfh == NULL)
            {
                fprintf(stderr,
                        "%s is already running %jd\n",
                        program,
                        (intmax_t)otherpid);
                exit(EXIT_FAILURE);
            }
        }
        
        if (daemon(0, 0) == -1)
        {
            fprintf(stderr, "daemonize failed\n");

            exitAndRemovePidFile(EXIT_FAILURE, pfh);
        }

        if (pfh)
        {
            pidfile_write(pfh);
        }

        openlog(program, LOG_PID, LOG_USER);
    }

    //---------------------------------------------------------------------

    if (signal(SIGINT, signalHandler) == SIG_ERR)
    {
        perrorLog(isDaemon, program, "installing SIGINT signal handler");

        exitAndRemovePidFile(EXIT_FAILURE, pfh);
    }

    //---------------------------------------------------------------------

    if (signal(SIGTERM, signalHandler) == SIG_ERR)
    {
        perrorLog(isDaemon, program, "installing SIGTERM signal handler");

        exitAndRemovePidFile(EXIT_FAILURE, pfh);
    }

    //---------------------------------------------------------------------

    bcm_host_init();

    //---------------------------------------------------------------------

    int result = 0;

    //---------------------------------------------------------------------
    // Make sure the VC_DISPLAY variable isn't set. 

    unsetenv("VC_DISPLAY");

    //---------------------------------------------------------------------

    DISPMANX_DISPLAY_HANDLE_T sourceDisplay
        = vc_dispmanx_display_open(sourceDisplayNumber);

    if (sourceDisplay == 0)
    {
        messageLog(isDaemon,
                   program,
                   LOG_ERR,
                   "open source display failed");
        exitAndRemovePidFile(EXIT_FAILURE, pfh);
    }

    DISPMANX_MODEINFO_T sourceInfo;

    result = vc_dispmanx_display_get_info(sourceDisplay, &sourceInfo);

    if (result != 0)
    {
        messageLog(isDaemon,
                   program,
                   LOG_ERR,
                   "getting source display dimensions failed");

        exitAndRemovePidFile(EXIT_FAILURE, pfh);
    }

    //---------------------------------------------------------------------

    DISPMANX_DISPLAY_HANDLE_T destDisplay
        = vc_dispmanx_display_open(destDisplayNumber);

    if (destDisplay == 0)
    {
        messageLog(isDaemon,
                   program,
                   LOG_ERR,
                   "open destination display failed");
        exitAndRemovePidFile(EXIT_FAILURE, pfh);
    }

    DISPMANX_MODEINFO_T destInfo;

    result = vc_dispmanx_display_get_info(destDisplay, &destInfo);

    if (result != 0)
    {
        messageLog(isDaemon,
                   program,
                   LOG_ERR,
                   "getting destination display dimensions failed");
        exitAndRemovePidFile(EXIT_FAILURE, pfh);
    }

    //---------------------------------------------------------------------

    messageLog(isDaemon,
               program,
               LOG_INFO,
               "copying from [%d] %dx%d to [%d] %dx%d",
               sourceDisplayNumber,
               sourceInfo.width,
               sourceInfo.height,
               destDisplayNumber,
               destInfo.width,
               destInfo.height);

    //---------------------------------------------------------------------

    uint32_t image_ptr;

    DISPMANX_RESOURCE_HANDLE_T resource = 
        vc_dispmanx_resource_create(VC_IMAGE_RGBA32,
                                    destInfo.width,
                                    destInfo.height,
                                    &image_ptr);

    //---------------------------------------------------------------------

    VC_RECT_T sourceRect;
    vc_dispmanx_rect_set(&sourceRect,
                         0,
                         0,
                         destInfo.width << 16,
                         destInfo.height << 16);
    
    VC_RECT_T destRect;
    vc_dispmanx_rect_set(&destRect, 0, 0, 0, 0);

    //---------------------------------------------------------------------

    VC_DISPMANX_ALPHA_T alpha =
    {
        DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS,
        255,
        0
    };

    DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);

    if (update == 0)
    {
        messageLog(isDaemon,
                   program,
                   LOG_ERR,
                   "display update failed");
        exitAndRemovePidFile(EXIT_FAILURE, pfh);
    }

    DISPMANX_ELEMENT_HANDLE_T element;
    element = vc_dispmanx_element_add(update,
                                      destDisplay,
                                      layerNumber,
                                      &destRect,
                                      resource,
                                      &sourceRect,
                                      DISPMANX_PROTECTION_NONE,
                                      &alpha,
                                      NULL,
                                      DISPMANX_NO_ROTATE);
    if (element == 0)
    {
        messageLog(isDaemon,
                   program,
                   LOG_ERR,
                   "failed to create DispmanX element");
        exitAndRemovePidFile(EXIT_FAILURE, pfh);
    }

    vc_dispmanx_update_submit_sync(update);

    //---------------------------------------------------------------------

    struct timeval start_time;
    struct timeval end_time;
    struct timeval elapsed_time;

    //---------------------------------------------------------------------

    while (run)
    {
        gettimeofday(&start_time, NULL);

        //-----------------------------------------------------------------

        result = vc_dispmanx_snapshot(sourceDisplay,
                                      resource,
                                      DISPMANX_NO_ROTATE);

        if (result != 0)
        {
            messageLog(isDaemon,
                       program,
                       LOG_ERR,
                       "DispmanX snapshot failed");
            exitAndRemovePidFile(EXIT_FAILURE, pfh);
        }

        //-----------------------------------------------------------------

        update = vc_dispmanx_update_start(0);

        if (update == 0)
        {
            messageLog(isDaemon,
                       program,
                       LOG_ERR,
                       "display update failed");
            exitAndRemovePidFile(EXIT_FAILURE, pfh);
        }

        vc_dispmanx_element_change_source(update, element, resource);
        vc_dispmanx_update_submit_sync(update);

        //-----------------------------------------------------------------

        gettimeofday(&end_time, NULL);
        timersub(&end_time, &start_time, &elapsed_time);

        if (elapsed_time.tv_sec == 0)
        {
            if (elapsed_time.tv_usec < frameDuration)
            {
                usleep(frameDuration -  elapsed_time.tv_usec);
            }
        }
    }

    //---------------------------------------------------------------------

    update = vc_dispmanx_update_start(0);
    vc_dispmanx_element_remove(update, element);
    vc_dispmanx_update_submit_sync(update);

    vc_dispmanx_resource_delete(resource);

    vc_dispmanx_display_close(sourceDisplay);
    vc_dispmanx_display_close(destDisplay);

    //---------------------------------------------------------------------

    messageLog(isDaemon, program, LOG_INFO, "exiting");

    if (isDaemon)
    {
        closelog();
    }

    if (pfh)
    {
        pidfile_remove(pfh);
    }

    //---------------------------------------------------------------------

    return 0 ;
}