Index: trunk/kernel/devices/dev_fbf.c
===================================================================
--- trunk/kernel/devices/dev_fbf.c	(revision 647)
+++ trunk/kernel/devices/dev_fbf.c	(revision 657)
@@ -2,5 +2,5 @@
  * dev_fbf.c - FBF (Frame Buffer) generic device API implementation.
  * 
- * Author  Alain Greiner    (2016,2017,2018,2019)
+ * Author  Alain Greiner    (2016,2017,2018,2019,2020)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -26,7 +26,12 @@
 #include <hal_gpt.h>
 #include <hal_drivers.h>
+#include <hal_irqmask.h>
+#include <hal_macros.h>
+#include <hal_uspace.h>
+#include <hal_vmm.h>
 #include <thread.h>
 #include <printk.h>
 #include <string.h>
+#include <memcpy.h>
 #include <chdev.h>
 #include <dev_fbf.h>
@@ -41,8 +46,12 @@
 char * dev_fbf_cmd_str( uint32_t cmd_type )
 {
-    if     ( cmd_type == FBF_READ       )  return "READ";
-    else if( cmd_type == FBF_WRITE      )  return "WRITE";
-    else if( cmd_type == FBF_GET_CONFIG )  return "GET_CONFIG";
-    else                                   return "undefined";
+    if     ( cmd_type == FBF_GET_CONFIG     )  return "GET_CONFIG";
+    else if( cmd_type == FBF_CREATE_WINDOW  )  return "CREATE_WINDOW";
+    else if( cmd_type == FBF_DELETE_WINDOW  )  return "DELETE_WINDOW";
+    else if( cmd_type == FBF_MOVE_WINDOW    )  return "MOVE_WINDOW";
+    else if( cmd_type == FBF_REFRESH_WINDOW )  return "REFRESH_WINDOW";
+    else if( cmd_type == FBF_DIRECT_WRITE   )  return "DIRECT_WRITE";
+    else if( cmd_type == FBF_DIRECT_READ    )  return "DIRECT_READ";
+    else                                       return "undefined";
 }
 
@@ -50,8 +59,27 @@
 void dev_fbf_init( chdev_t  * fbf )
 {
+    uint32_t wid;
+
     // set chdev name
     strcpy( fbf->name, "fbf" );
 
-    // call driver init function
+    // initialize lock protecting the windows
+    remote_rwlock_init( XPTR( local_cxy , &fbf->ext.fbf.windows_lock ),
+                        LOCK_FBF_WINDOWS ); 
+
+    // initialize root of windows xlist
+    xlist_root_init( XPTR( local_cxy , &fbf->ext.fbf.windows_root ) );
+
+    // initialize windows_tbl[] array
+    for( wid = 0 ; wid < CONFIG_FBF_WINDOWS_MAX_NR ; wid++ ) 
+    {
+        fbf->ext.fbf.windows_tbl[wid] = XPTR_NULL;
+    }
+
+    // initialize wid allocator bitmap
+    bitmap_init( fbf->ext.fbf.windows_bitmap , CONFIG_FBF_WINDOWS_MAX_NR );
+
+    // call driver init function to initialize the harware FBF
+    // and initialize the width, height, and subsampling FBF chdev fields
     hal_drivers_fbf_init( fbf );
 
@@ -66,5 +94,5 @@
     xptr_t  dev_xp = chdev_dir.fbf[0];
 
-    assert( (dev_xp != XPTR_NULL) , "undefined FBF chdev descriptor" );
+assert( (dev_xp != XPTR_NULL) , "undefined FBF chdev descriptor" );
 
     // get FBF chdev cluster and local pointer
@@ -79,19 +107,282 @@
 }  // end dev_fbf_get_config()
 
-/////////////////////////////////////////////////////
-error_t dev_fbf_move_data( uint32_t   cmd_type,
-                           void     * user_buffer,
-                           uint32_t   length,
-                           uint32_t   offset )
-{
-    // get pointer on calling thread
-    thread_t * this = CURRENT_THREAD;
+///////////////////////////////////////////////
+uint32_t dev_fbf_create_window( uint32_t   nlines,
+                                uint32_t   npixels,
+                                uint32_t   l_min,
+                                uint32_t   p_min,
+                                intptr_t * user_buffer )
+{
+    kmem_req_t     req;
+    fbf_window_t * window;      // window descriptor (created in local cluster)
+    vseg_t       * vseg;        // vseg descriptor (created in reference cluster)
+    intptr_t       vseg_base;   // vseg base address in user space  
+
+    // get local pointers on calling thread and process
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
 
 #if DEBUG_DEV_FBF
 uint32_t   cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_DEV_FBF < cycle )
-printk("\n[%s] thread[%x,%x] : %s / buffer %x / length %d / offset %x / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid,  
-dev_fbf_cmd_str(cmd_type), user_buffer, length, offset, cycle );
+printk("\n[%s] thread[%x,%x] enter : nlines %d / npixels %d / l_min %d / p_min %d / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, nlines, npixels, l_min, p_min, cycle );
+#endif
+
+    // get cluster and pointers on FBF chdev
+    xptr_t      fbf_xp  = chdev_dir.fbf[0];
+    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
+    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
+
+// check fbf_xp definition
+assert( (fbf_xp != XPTR_NULL) , "undefined FBF chdev descriptor" );
+
+    // get FBF width and height
+    uint32_t fbf_width  = hal_remote_l32( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.width ) );
+    uint32_t fbf_height = hal_remote_l32( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.height ) );
+
+    // check new window size and coordinates
+    if( (((l_min + nlines) > fbf_height) || ((p_min + npixels) > fbf_width)) )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x]" 
+        "illegal new coordinates (%d,%d) for window (%d,%d) in fbf (%d,%d)\n",
+        process->pid, this->trdid, p_min, l_min, npixels, nlines, fbf_width, fbf_height );
+        return -1;
+    }
+
+    // build extended pointers on windows lock, root, and wid allocator 
+    xptr_t windows_lock_xp   = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock );
+    xptr_t windows_root_xp   = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_root );
+    xptr_t windows_bitmap_xp = XPTR( fbf_cxy ,  fbf_ptr->ext.fbf.windows_bitmap );
+ 
+    // allocate memory for the window descriptor in local cluster
+    req.type   = KMEM_KCM;
+    req.order  = bits_log2( sizeof(fbf_window_t) );
+    req.flags  = AF_ZERO | AF_KERNEL;
+    window     = kmem_alloc( &req );
+
+    if( window == NULL )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] cannot allocate window descriptor\n",
+        __FUNCTION__, process->pid, this->trdid );
+        return -1;
+    }
+
+#if (DEBUG_DEV_FBF & 1)
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] created window descriptor %x / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, window, cycle );
+#endif
+
+    // getpointers on reference process
+    xptr_t      ref_xp  = process->ref_xp;
+    process_t * ref_ptr = GET_PTR( ref_xp );
+    cxy_t       ref_cxy = GET_CXY( ref_xp );
+
+    // allocate a new vseg, and introduce it in the reference process VSL
+    if( ref_cxy == local_cxy )
+    {
+        vseg = vmm_create_vseg( process,            // owner process
+                                VSEG_TYPE_ANON,     // localised, public
+                                0,                  // base, unused for ANON
+                                nlines * npixels,   // size
+                                0,                  // file_offset, unused for ANON 
+                                0,                  // file_size, unused for ANON
+                                XPTR_NULL,          // mapper_xp, unused for ANON
+                                local_cxy );        // mapping cluster
+    }
+    else
+    {
+        rpc_vmm_create_vseg_client( ref_cxy,
+                                    ref_ptr,
+                                    VSEG_TYPE_ANON,
+                                    0,                 // base, unused for ANON
+                                    nlines * npixels,  // size
+                                    0,                 // file_offset, unused for ANON
+                                    0,                 // file size, unused for ANON
+                                    XPTR_NULL,         // mapper_xp, unused for ANON
+                                    local_cxy,
+                                    &vseg );
+    } 
+        
+    if( vseg == NULL )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] cannot create vseg in reference cluster\n",
+        __FUNCTION__, process->pid, this->trdid );
+        req.ptr = (void *)window;
+        kmem_free( &req );
+        return -1;
+    }
+
+    // get vseg base
+    vseg_base = (intptr_t)hal_remote_lpt( XPTR( ref_cxy , &vseg->min ) );
+
+#if (DEBUG_DEV_FBF & 1)
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] allocated vseg / base %x / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, vseg_base, cycle );
+#endif
+
+    // take the lock protecting windows in write mode
+    remote_rwlock_wr_acquire( windows_lock_xp );
+
+    // allocate a wid from allocator in FBF descriptor extension
+    uint32_t wid = bitmap_remote_alloc( windows_bitmap_xp , CONFIG_FBF_WINDOWS_MAX_NR );
+
+    if( wid == 0xFFFFFFFF )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] cannot allocate buffer for window\n",
+        __FUNCTION__, process->pid, this->trdid );
+        req.ptr = (void *)window;
+        kmem_free( &req );
+        vmm_remove_vseg( process , vseg );
+        return -1;
+    }
+ 
+    // initialize window descriptor
+    window->pid     = process->pid;
+    window->wid     = wid;
+    window->height  = nlines;
+    window->width   = npixels;
+    window->l_min   = l_min;
+    window->p_min   = p_min;
+    window->hidden  = false;
+    window->buffer  = (uint8_t *)vseg_base;
+
+    // register new window in xlist rooted in FBF extension
+    xlist_add_last( windows_root_xp , XPTR( local_cxy , &window->xlist ) );
+
+    // build extended pointer on relevant entry in windows_tbl[] array
+    xptr_t windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] );
+
+    // register new window in windows_tbl[] stored in FBF extension
+    hal_remote_s64( windows_tbl_xp , XPTR( local_cxy , window ) );
+
+    // release the lock protecting windows in write mode
+    remote_rwlock_wr_release( windows_lock_xp );
+
+#if DEBUG_DEV_FBF
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] exit / wid %d / buffer %x / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, wid , window->buffer, cycle );
+#endif
+
+#if (DEBUG_DEV_FBF & 1)
+hal_vmm_display( ref_xp , true );
+#endif
+
+    // return pointer on allocated buffer
+    *user_buffer = vseg_base;
+
+    return wid;
+
+}  // end dev_fbf_create_window()
+
+////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by the dev_fbf_display() function.
+// For a partial FBF line, identified by the <f_line>, <f_p_min>, <f_p_max> arguments
+// in FBF reference, and for one remote window, identified by the <window_xp> argument,
+// it updates the target buffer identified by <t_buffer>, and containing exactly
+// (f_p_max - f_p_min) pixels. 
+// Depending on the actual overlap between the window and the <t_buffer> representing 
+// the partial FBF line, it moves up to (f_p_max - f_p_min) pixels from the window 
+// buffer to the target buffer. The number of moved pixels can be nul if no overlap.
+////////////////////////////////////////////////////////////////////////////////////////
+// @ f_line     : [in]  line index in FBF reference (from 0 to fbf_height-1).
+// @ f_p_min    : [in]  first pixel in FBF line.
+// @ f_p_max    : [in]  last pixel in FBF line (excluded). 
+// @ window_xp  : [in]  extended pointer on checked window .
+// @ t_buffer   : [out] local pointer on target buffer to be updated.
+///////////////////////////////////////////////////////////////////////////////////////
+__attribute__ ((noinline)) static void handle_one_window( uint32_t  f_line,
+                                                          uint32_t  f_p_min,
+                                                          uint32_t  f_p_max,
+                                                          xptr_t    window_xp,
+                                                          uint8_t * t_buffer )
+{
+    // get remote window descriptor cluster and local pointer
+    cxy_t          window_cxy = GET_CXY( window_xp );
+    fbf_window_t * window_ptr = GET_PTR( window_xp );
+
+    // get remote window min/max coordinates in FBF reference
+    uint32_t  w_l_min    = hal_remote_l32( XPTR( window_cxy , &window_ptr->l_min ) );
+    uint32_t  w_p_min    = hal_remote_l32( XPTR( window_cxy , &window_ptr->p_min ) );
+    uint32_t  w_height   = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) );
+    uint32_t  w_width    = hal_remote_l32( XPTR( window_cxy , &window_ptr->width  ) );
+    uint32_t  w_l_max    = w_l_min + w_height;
+    uint32_t  w_p_max    = w_p_min + w_width;
+
+    // does nothing if partial FBF line does not overlap the window
+    if( (f_line < w_l_min)  || (f_line >= w_l_max) ||
+        (f_p_max < w_p_min) || (f_p_min >= w_p_max) ) return;
+
+    // get pointer on window buffer in user space
+    uint8_t * w_buffer = hal_remote_lpt( XPTR( window_cxy , &window_ptr->buffer ) ); 
+
+    // get min & max indexes for pixels to be moved in FBF reference
+    uint32_t f_pixel_min = (f_p_min < w_p_min) ? w_p_min : f_p_min;
+    uint32_t f_pixel_max = (f_p_max < w_p_max) ? f_p_max : w_p_max;
+
+    // compute number of pixels to move from w_buffer to f_buffer
+    uint32_t npixels = f_pixel_max - f_pixel_min;
+
+    // compute offset in target buffer
+    uint32_t t_offset = f_pixel_min - f_p_min; 
+
+    // compute line index in window  
+    uint32_t w_line = f_line - w_l_min;
+
+    // compute offset in window buffer
+    uint32_t w_offset = (w_line * w_height) + f_pixel_min - w_p_min;
+
+    // move pixels from w_buffer (user space) to t_buffer in kernel space
+    hal_copy_from_uspace( XPTR( local_cxy  , &t_buffer[t_offset]  ),
+                          &w_buffer[w_offset], 
+                          npixels );
+
+}  // end handle_one_window()
+
+////////////////////////////////////////////////////////////////////////////////////////
+// This static function is called by dev_fbf_refresh_window(), dev_fbf_move_window(),
+// dev_fbf_resize_window(), and dev_fbf_delete_window(). It updates all lines of the
+// window identified by the <window_xp>, <line_first>, and <line_last>> arguments. 
+// It scan all registered windows to take into account the overlap priorities defined 
+// by the windows xlist. It does not take the lock protecting the xlist, that must be 
+// taken by the calling function.
+////////////////////////////////////////////////////////////////////////////////////////
+// @ window_xp  : [in]  extended pointer on window defining the FBF pixels to refresh.
+// @ line_first : [in]  first line index.
+// @ line_last  : [in]  last line index (excluded).
+////////////////////////////////////////////////////////////////////////////////////////
+error_t fbf_update( xptr_t    window_xp,
+                    uint32_t  line_first,
+                    uint32_t  line_last )
+{
+    uint32_t       line;                // iterator to scan the FBF lines
+    uint32_t       pixel;               // iterator to scan pixels in one FBF line
+    xptr_t         iter_xp;             // iterator to scan the list of windows
+    error_t        error;
+
+    // this intermediate buffer stores one line in
+    // target window, to handle other windows overlap
+    uint8_t       line_buffer[CONFIG_FBF_WINDOWS_MAX_WIDTH];
+
+    // get pointer on calling thread and core lid
+    thread_t  * this = CURRENT_THREAD;
+
+    // get window cluster and local pointer
+    cxy_t          window_cxy = GET_CXY( window_xp );
+    fbf_window_t * window_ptr = GET_PTR( window_xp );
+
+#if DEBUG_DEV_FBF
+uint32_t wid   = hal_remote_l32( XPTR( window_cxy , &window_ptr->wid ) );
+uint32_t lid   = this->core->lid;
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] core[%x,%d] enter / wid %d / cycle %d\n",
+__FUNCTION__, local_cxy, lid, wid, cycle );
 #endif
 
@@ -101,6 +392,553 @@
     chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
 
-// check fbf_xp definition
-assert( (fbf_xp != XPTR_NULL) , "undefined FBF chdev descriptor" );
+    // get frame buffer width
+    uint32_t fbf_width  = hal_remote_l32( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.width ) );
+
+    // get pointer on driver command function
+    dev_cmd_t * cmd = hal_remote_lpt( XPTR( fbf_cxy , &fbf_ptr->cmd ) );
+
+    // build extended pointers on windows xlist root
+    xptr_t  windows_root_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_root );
+
+    // get window size and coordinates
+    uint32_t  p_min     = hal_remote_l32( XPTR( window_cxy , &window_ptr->p_min ) );
+    uint32_t  l_min     = hal_remote_l32( XPTR( window_cxy , &window_ptr->l_min ) );
+    uint32_t  w_pixels  = hal_remote_l32( XPTR( window_cxy , &window_ptr->width  ) );
+
+    error = 0;
+
+    // loop on target window lines (FBF coordinates) 
+    for( line = l_min + line_first ; line < (l_min + line_last) ; line++ )
+    {
+        // reset the line buffer to default value
+        for( pixel = 0 ; pixel < w_pixels ; pixel++ ) line_buffer[pixel] = 127;
+
+        // loop on all windows
+        XLIST_FOREACH( windows_root_xp , iter_xp )
+        {
+            // get pointers on remote window
+            xptr_t         tgt_xp  = XLIST_ELEMENT( iter_xp , fbf_window_t , xlist );
+            fbf_window_t * tgt_ptr = GET_PTR( window_xp );
+            cxy_t          tgt_cxy = GET_CXY( window_xp );
+
+            bool_t hidden = hal_remote_l32( XPTR( tgt_cxy , &tgt_ptr->hidden ) );
+
+            // fill the line_buf for this window if not hidden
+            if( hidden == false ) handle_one_window( line,               // line index
+                                                     p_min,              // pixel_min
+                                                     p_min + w_pixels,   // pixel_max 
+                                                     tgt_xp,             // window_xp      
+                                                     line_buffer );      
+        }  // end for windows
+
+        // compute offset in FBF
+        uint32_t fbf_offset = p_min + (line * fbf_width);
+
+        // register command in calling thread descriptor
+        this->fbf_cmd.dev_xp    = fbf_xp;
+        this->fbf_cmd.type      = FBF_DRIVER_KERNEL_WRITE;
+        this->fbf_cmd.buffer    = line_buffer;
+        this->fbf_cmd.npixels   = w_pixels;
+        this->fbf_cmd.offset    = fbf_offset;
+
+        // call driver to display one line
+        cmd( XPTR( local_cxy , this ) );
+
+        error |= this->fbf_cmd.error;
+    
+    }  // end for lines
+
+#if DEBUG_DEV_FBF
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] core[%x,%d] exit / wid %d / cycle %d\n",
+__FUNCTION__, local_cxy, this->core->lid, wid, cycle );
+#endif
+
+    // return I/O operation status
+    return error;
+
+}  // end fbf_update()
+
+//////////////////////////////////////////////
+error_t dev_fbf_delete_window( uint32_t  wid )
+{
+    kmem_req_t     req;
+
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+#if DEBUG_DEV_FBF
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] enters : wid %d / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, wid, cycle );
+#endif
+
+    // get cluster and pointers on FBF chdev
+    xptr_t      fbf_xp  = chdev_dir.fbf[0];
+    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
+    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
+
+    // build extended pointers on windows lock, and wid allocator 
+    xptr_t windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock );
+    xptr_t wid_bitmap_xp   = XPTR( fbf_cxy ,  fbf_ptr->ext.fbf.windows_bitmap );
+
+    // build extended pointer on relevant entry in windows_tbl[] array
+    xptr_t  windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] );
+
+    // get extended pointer on remote window descriptor
+    xptr_t window_xp  = hal_remote_l64( windows_tbl_xp );
+
+    if( window_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] / wid %d non registered\n",
+        __FUNCTION__, process->pid, this->trdid, wid );
+        return -1;
+    }
+
+    // get cluster and local pointer on remote window
+    cxy_t          window_cxy = GET_CXY( window_xp );
+    fbf_window_t * window_ptr = GET_PTR( window_xp );
+
+    // get process owner PID
+    pid_t owner_pid = hal_remote_l32( XPTR( window_cxy , &window_ptr->pid ) );
+
+    // check caller PID / owner PID
+    if( owner_pid != process->pid )
+    {
+        printk("\n[ERROR] in %s : caller PID (%x) != owner PID (%x)\n",
+        __FUNCTION__, process->pid , owner_pid );
+        return -1;
+    }
+
+    // get associated buffer, and number of lines
+    uint8_t  * buffer = hal_remote_lpt( XPTR( window_cxy , &window_ptr->buffer ) );
+    uint32_t   nlines = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) );
+
+    // 1. take the lock protecting windows in write mode
+    remote_rwlock_wr_acquire( windows_lock_xp );
+
+    // 2. update the FBF window
+    fbf_update( window_xp , 0 , nlines );
+
+    // 3. remove the window from windows_tbl[] array
+    hal_remote_s64( windows_tbl_xp , XPTR_NULL );
+
+    // 4. remove the window from xlist      
+    xlist_unlink( XPTR( window_cxy , &window_ptr->xlist ) );
+
+    // 5. release wid to bitmap
+    bitmap_remote_clear( wid_bitmap_xp , wid );
+
+    // 6. release the lock protecting windows in write mode
+    remote_rwlock_wr_release( windows_lock_xp );
+ 
+    // 7. release memory allocated for window descriptor
+    req.type = KMEM_KCM;
+    req.ptr  = window_ptr;
+    kmem_remote_free( window_cxy , &req );
+
+    // 8. release the associated vseg
+    vmm_global_delete_vseg( process , (intptr_t)buffer );
+    
+#if DEBUG_DEV_FBF
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, cycle );
+#endif
+
+    return 0;
+
+}  // end dev_fbf_delete_window()
+
+////////////////////////////////////////////
+error_t dev_fbf_move_window( uint32_t  wid,
+                             uint32_t  l_min,
+                             uint32_t  p_min )
+{
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+#if DEBUG_DEV_FBF
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] enters : wid %d / l_min %d / p_min %d / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, wid, l_min, p_min, cycle );
+#endif
+
+    // get cluster and pointers on FBF chdev
+    xptr_t      fbf_xp  = chdev_dir.fbf[0];
+    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
+    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
+
+    // build extended pointers on windows lock and root 
+    xptr_t windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock );
+    xptr_t windows_root_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_root );
+
+    // build extended pointer on relevant entry in windows_tbl[] array
+    xptr_t  windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] );
+
+    // get extended pointer on remote window descriptor
+    xptr_t window_xp  = hal_remote_l64( windows_tbl_xp );
+
+    if( window_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] / wid %d non registered\n",
+        __FUNCTION__, process->pid, this->trdid, wid );
+        return -1;
+    }
+
+    // get cluster and local pointer for remote window
+    cxy_t          window_cxy = GET_CXY( window_xp );
+    fbf_window_t * window_ptr = GET_PTR( window_xp );
+
+    // get process owner PID, coordinates, and number of lines
+    pid_t    owner_pid = hal_remote_l32( XPTR( window_cxy , &window_ptr->pid ) );
+    uint32_t p_zero    = hal_remote_l32( XPTR( window_cxy , &window_ptr->p_min ) );
+    uint32_t l_zero    = hal_remote_l32( XPTR( window_cxy , &window_ptr->l_min ) );
+    uint32_t nlines    = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) );
+
+    // check caller PID / owner PID
+    if( owner_pid != process->pid )
+    {
+        printk("\n[ERROR] in %s : caller PID (%x) != owner PID (%x)\n",
+        __FUNCTION__, process->pid , owner_pid );
+        return -1;
+    }
+
+    // does nothing if no change
+    if( (p_zero == p_min) && (l_zero == l_min) )  return 0;
+
+    // 1. take the lock protecting windows in write mode
+    remote_rwlock_wr_acquire( windows_lock_xp );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] lock taken\n", __FUNCTION__ );
+#endif
+
+    // 2. gives the window the lowest priority 
+    xptr_t xlist_entry_xp =  XPTR( window_cxy , &window_ptr->xlist );
+    xlist_unlink( xlist_entry_xp );
+    xlist_add_first( windows_root_xp , xlist_entry_xp );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] set low priority \n", __FUNCTION__ );
+#endif
+
+    // 3. set the "hidden" flag in window descriptor
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->hidden ) , true );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] hidden set\n", __FUNCTION__ );
+#endif
+
+    // 4. refresh the FBF for the current window position
+    fbf_update( window_xp , 0 , nlines );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] refreshed old position\n", __FUNCTION__ );
+#endif
+
+    // 5. set the new coordinates in the window descriptor,
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->l_min ), l_min );
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->p_min ), p_min );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] l_min & p_min updated\n", __FUNCTION__ );
+#endif
+
+    // 6. gives the window the highest priority 
+    xlist_unlink( xlist_entry_xp );
+    xlist_add_last( windows_root_xp , xlist_entry_xp );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] set high priority\n", __FUNCTION__ );
+#endif
+
+    // 7. reset the "hidden" flag in window descriptor
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->hidden ) , false );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] hidden reset\n", __FUNCTION__ );
+#endif
+
+    // 8. refresh the FBF for the new window position
+    fbf_update( window_xp , 0 , nlines );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] refresh new position\n", __FUNCTION__ );
+#endif
+
+    // 9. release the lock protecting windows in write mode
+    remote_rwlock_wr_release( windows_lock_xp );
+ 
+#if DEBUG_DEV_FBF
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, cycle );
+#endif
+
+    return 0;
+
+}  // end dev_fbf_move_window()
+
+/////////////////////////////////////////////
+error_t dev_fbf_resize_window( uint32_t  wid,
+                               uint32_t  width,
+                               uint32_t  height )
+{
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+#if DEBUG_DEV_FBF
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] enters : wid %d / width %d / height %d / cycle %d\n",
+__FUNCTION__ , process->pid , this->trdid , wid, width , height , cycle );
+#endif
+
+    // get cluster and pointers on FBF chdev
+    xptr_t      fbf_xp  = chdev_dir.fbf[0];
+    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
+    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
+
+    // build extended pointers on windows lock and root 
+    xptr_t windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock );
+    xptr_t windows_root_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_root );
+
+    // build extended pointer on relevant entry in windows_tbl[] array
+    xptr_t  windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] );
+
+    // get extended pointer on remote window descriptor
+    xptr_t window_xp  = hal_remote_l64( windows_tbl_xp );
+
+    if( window_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] / wid %d non registered\n",
+        __FUNCTION__, process->pid, this->trdid, wid );
+        return -1;
+    }
+
+    // get cluster and local pointer for remote window
+    cxy_t          window_cxy = GET_CXY( window_xp );
+    fbf_window_t * window_ptr = GET_PTR( window_xp );
+
+    // get process owner PID, width, height, and buffer
+    pid_t    owner_pid = hal_remote_l32( XPTR( window_cxy , &window_ptr->pid ) );
+    uint32_t nlines    = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) );
+    uint32_t npixels   = hal_remote_l32( XPTR( window_cxy , &window_ptr->width ) );
+    void   * base      = hal_remote_lpt( XPTR( window_cxy , &window_ptr->buffer ) );
+
+    // check caller PID / owner PID
+    if( owner_pid != process->pid )
+    {
+        printk("\n[ERROR] in %s : caller PID (%x) != owner PID (%x)\n",
+        __FUNCTION__, process->pid , owner_pid );
+        return -1;
+    }
+
+    // does nothing if no change
+    if( (width == npixels) && (height == nlines) ) return 0;
+
+    // compute old_size and new size
+    uint32_t old_size = nlines * npixels;
+    uint32_t new_size = width * height;
+
+    // 1. take the lock protecting windows in write mode
+    remote_rwlock_wr_acquire( windows_lock_xp );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] lock taken\n", __FUNCTION__ );
+#endif
+
+    // 2. gives the window the lowest priority (remove, then add first)
+    xptr_t xlist_entry_xp =  XPTR( window_cxy , &window_ptr->xlist );
+    xlist_unlink( xlist_entry_xp );
+    xlist_add_first( windows_root_xp , xlist_entry_xp );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] set low priority\n", __FUNCTION__ );
+#endif
+
+    // 3. set the "hidden" flag in window descriptor
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->hidden ) , true );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] hidden set\n", __FUNCTION__ );
+#endif
+
+    // 4. refresh the FBF for the current window size
+    fbf_update( window_xp , 0 , nlines );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] refreshed old window\n", __FUNCTION__ );
+#endif
+
+    // 5. set the new width & height in the window descriptor,
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->width  ), width );
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->height ), height );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] width & height updated\n", __FUNCTION__ );
+#endif
+
+    // 6. resize vseg if required
+    vmm_global_resize_vseg( process, (intptr_t)base, (intptr_t)base, width * height );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] vseg resized\n", __FUNCTION__ );
+#endif
+
+    // 7. fill buffer extension if required
+    if( new_size > old_size )  memset( base + old_size , 0 , new_size - old_size );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] buffer extension initialized\n", __FUNCTION__ );
+#endif
+
+    // 8. gives the window the highest priority 
+    xlist_unlink( xlist_entry_xp );
+    xlist_add_last( windows_root_xp , xlist_entry_xp );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] set high priority\n", __FUNCTION__ );
+#endif
+
+    // 9. reset the "hidden" flag in window descriptor
+    hal_remote_s32( XPTR( window_cxy , &window_ptr->hidden ) , false );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] hidden reset\n", __FUNCTION__ );
+#endif
+
+    // 10. refresh the FBF for the new window position
+    fbf_update( window_xp , 0 , height );
+
+#if ( DEBUG_DEV_FBF & 1 )
+printk("\n[%s] refresh new position\n", __FUNCTION__ );
+#endif
+
+    // 11. release the lock protecting windows in write mode
+    remote_rwlock_wr_release( windows_lock_xp );
+ 
+#if DEBUG_DEV_FBF
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, cycle );
+#endif
+
+    return 0;
+
+}  // end dev_fbf_resize_window()
+
+///////////////////////////////////////////////
+error_t dev_fbf_refresh_window( uint32_t  wid,
+                                uint32_t  line_first,
+                                uint32_t  line_last )
+{
+    // get local pointers on calling thread and process
+    thread_t  * this    = CURRENT_THREAD;
+    process_t * process = this->process;
+
+#if DEBUG_DEV_FBF
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] enters for wid %d / first %d / last %d / cycle %d\n",
+__FUNCTION__ , process->pid, this->trdid, wid, line_first, line_last, cycle );
+#endif
+
+    // get cluster and pointers on FBF chdev
+    xptr_t      fbf_xp  = chdev_dir.fbf[0];
+    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
+    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
+
+    // build extended pointer on windows lock
+    xptr_t  windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock );
+
+    // build extended pointer on relevant entry in windows_tbl[] array
+    xptr_t  windows_tbl_xp  = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] );
+
+    // get pointers on remote window descriptor
+    xptr_t         window_xp  = hal_remote_l64( windows_tbl_xp );
+    cxy_t          window_cxy = GET_CXY( window_xp );
+    fbf_window_t * window_ptr = GET_PTR( window_xp );
+
+    // check <wid> argument
+    if( window_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s / thread[%x,%x] / wid %d non registered\n",
+        __FUNCTION__, process->pid, this->trdid, wid );
+        return -1;
+    }
+
+    // get process owner PID
+    pid_t owner_pid = hal_remote_l32( XPTR( window_cxy , &window_ptr->pid ) );
+
+    // check caller PID / owner PID
+    if( owner_pid != process->pid )
+    {
+        printk("\n[ERROR] in %s : caller PID (%x) != owner PID (%x)\n",
+        __FUNCTION__, process->pid , owner_pid );
+        return -1;
+    }
+
+    // get number of lines in window
+    uint32_t nlines = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) ); 
+
+    // check <line_first> and <line_last> arguments
+    if( (line_first >= nlines) || (line_last > nlines) || (line_first >= line_last) )
+    {
+        printk("\n[ERROR] in %s : illegal (l_first %d , l_last %d) / height %d\n",
+        __FUNCTION__, line_first, line_last, nlines );
+        return -1;
+    }
+
+    // take the lock protecting windows xlist in read mode
+    remote_rwlock_rd_acquire( windows_lock_xp );
+
+    // update FBF 
+    fbf_update( window_xp , line_first , line_last );
+
+    // release the lock protecting windows xlist in write mode
+    remote_rwlock_rd_release( windows_lock_xp );
+
+#if DEBUG_DEV_FBF
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] exit for wid %d / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, wid, cycle );
+#endif
+
+    return 0;
+
+}  // end dev_fbf_refresh_window()
+
+///////////////////////////////////////////////
+// TODO Deprecated : january 2020 [AG]
+///////////////////////////////////////////////
+error_t dev_fbf_move_data( bool_t     is_write,
+                           void     * user_buffer,
+                           uint32_t   npixels,
+                           uint32_t   offset )
+{
+    // get pointer on calling thread
+    thread_t * this = CURRENT_THREAD;
+
+#if DEBUG_DEV_FBF
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_DEV_FBF < cycle )
+printk("\n[%s] thread[%x,%x] :  buffer %x / npixels %d / offset %x / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid,  
+user_buffer, npixels, offset, cycle );
+#endif
+
+    // get pointers on FBF chdev
+    xptr_t      fbf_xp  = chdev_dir.fbf[0];
+    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
+    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );
 
     // get frame buffer width and height
@@ -108,14 +946,18 @@
     uint32_t height = hal_remote_l32 ( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.height ) );
 
-// check offset and length versus FBF size
-assert( ((offset + length) <= (width * height)) ,
-"offset %d / length %d / width %d / height %d\n", offset, length, width, height ); 
+    // check offset and npixels versus FBF size
+    if( ((offset + npixels) > (width * height)) )
+    {
+        printk("\n[ERROR] in %s : offset (%d) + npixels (%d) / width (%d) / height (%d)\n",
+        __FUNCTION__, offset, npixels, width, height ); 
+        return -1;
+    }
 
     // register command in calling thread descriptor
     this->fbf_cmd.dev_xp    = fbf_xp;
-    this->fbf_cmd.type      = cmd_type;
+    this->fbf_cmd.type      = is_write ? FBF_DRIVER_USER_WRITE : FBF_DRIVER_USER_READ;
     this->fbf_cmd.buffer    = user_buffer;
     this->fbf_cmd.offset    = offset;
-    this->fbf_cmd.length    = length;
+    this->fbf_cmd.npixels   = npixels;
 
     // get driver command function
@@ -130,7 +972,6 @@
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_DEV_FBF < cycle )
-printk("\n[%s] thread[%x,%x] completes %s / error = %d / cycle %d\n",
-__FUNCTION__ , this->process->pid, this->trdid,  
-dev_fbf_cmd_str(cmd_type), error , cycle );
+printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, cycle );
 #endif
 
@@ -139,2 +980,4 @@
 
 }  // end dev_fbf_move_data()
+
+
