notcurses icon indicating copy to clipboard operation
notcurses copied to clipboard

API feels gimped, need ncplanes without parents

Open mulle-nat opened this issue 4 months ago • 2 comments

Currently it seems, I can't have a ncplane without a parent ncplane. There is one exception noted in the source: ncdirect gets internal API ncplane_new_internal to create a rootless plane. After all, such planes are convenient, but no dice for the stinking API coder :wink: .

The only way to maintain nccell information outside of a ncplane are unwieldy private ncplane/nccell copies (you can't reuse the ncplane struct , because a) its internal api and b) you need a parent). You also can't reuse nccell because it needs a plane for storage. So the API coder writes code like at the bottom of this issue (an extended rgb.c demo). The amount of copying going with a strdup for each cell is just not nice. With a parentless ncplane, a ncplane_copy function would just need to memcpy all the cells and strdup the egcpool or ?

With parentless planes, you can also do operations like temporarily hiding and showing a plane much easier:

// hide
oldparent = ncplane_parent( n);
ncplane_reparent( n, NULL);
// show
ncplane_reparent( n, oldparent)

should do the trick. Generally I would prefer to setup my ncplanes first completely "offscreen" so to speak and then assemble them myself in z order before rendering. That would be easy with rootless planes, impossible without.

I am not sure if all my troubles went away if ncplane could be rootless, but it feels like it.


#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <notcurses/notcurses.h>


struct cellcopy
{
   uint64_t   channels;
   char       *string;
   uint16_t   stylemask;
};


struct cellcopyarray
{
   struct cellcopy   *cells;
   unsigned int      width;
   unsigned int      height;
};


void   cellcopyarray_init_with_plane( struct cellcopyarray *array,
                                      struct ncplane *plane)
{
   unsigned int      rows, cols;
   struct cellcopy   *copies;
   struct cellcopy   *copy;
   struct nccell     c;

   if( ! array || ! plane)
     return;

   ncplane_dim_yx( plane, &rows, &cols);

   // Allocate memory for the new array of nccell structures
   copies = calloc( rows * cols, sizeof( struct cellcopy));
   if( copies == NULL)
   {
      *array = (struct cellcopyarray) { NULL, -1 , -1};
      return;
   }

   array->cells  = copies;
   array->width  = cols;
   array->height = rows;

   // Iterate over each cell in the ncplane
   for( unsigned int y = 0; y < rows; y++)
   {
      for( unsigned int x = 0; x < cols; x++)
      {
         c = (struct nccell) NCCELL_TRIVIAL_INITIALIZER;  // somehow needed
         if( ncplane_at_yx_cell( plane, y, x, &c) < 0)
         {
             free( array->cells); // Free the allocated memory in case of error
             *array = (struct cellcopyarray) { NULL, -1 , -1};
             return; // Failed to get cell at the specified coordinates
         }

         copy            = copies++;

         // Manually copy the cell contents
         copy->string    = nccell_strdup( plane, &c);

         // Copy the cell channels
         copy->channels  = c.channels;

         // Copy the cell style mask
         copy->stylemask = c.stylemask;

         nccell_release( plane, &c);
      }
   }
}


void  cellcopyarray_done( struct cellcopyarray *array)
{
   struct cellcopy   *copies;
   struct cellcopy   *sentinel;

   if( ! array)
      return;

   copies = array->cells;
   sentinel = &copies[  array->height * array->width];
   while( copies < sentinel)
   {
      free( copies->string);
      ++copies;
   }

   free( array->cells);
}


void   cellcopyarray_write_to_plane( struct cellcopyarray *array, struct ncplane *plane)
{
   struct nccell     c;
   struct cellcopy   *copy;
   struct cellcopy   *copies;
   unsigned int      rows, cols;
   unsigned int      x, y;

   if( ! array || ! plane)
      return;

   ncplane_dim_yx( plane, &rows, &cols);
   if( array->width != cols)
      abort();
   if( array->height != rows)
      abort();

   copies = array->cells;
    // Iterate over each cell in the array
   for( y = 0; y < (unsigned int) array->height; y++)
      for( x =  0; x < (unsigned int) array->width; x++)
      {
         // Get the cell from the array
         // Write the cell to the ncplane

         c = (struct nccell) NCCELL_TRIVIAL_INITIALIZER;  // somehow needed
         if( ncplane_at_yx_cell( plane, y, x, &c) < 0)
         {
            abort();
         }

         copy = copies++;
         nccell_prime( plane, &c,
                              copy->string,
                              copy->stylemask,
                              copy->channels);
         if( ncplane_putc_yx( plane, y, x, &c) < 0)
         {
            abort();
         }
         nccell_release( plane, &c);
      }
}



int main(void){
  if(!setlocale(LC_ALL, "")){
    fprintf(stderr, "Couldn't set locale\n");
    return EXIT_FAILURE;
  }
  struct notcurses_options opts = {
    .flags = NCOPTION_INHIBIT_SETLOCALE
             | NCOPTION_NO_ALTERNATE_SCREEN
             | NCOPTION_SUPPRESS_BANNERS
             | NCOPTION_DRAIN_INPUT,
  };
  struct notcurses* nc = notcurses_core_init(&opts, NULL);
  if(nc == NULL){
    return EXIT_FAILURE;
  }
  unsigned dimy, dimx;
  struct ncplane* plane = notcurses_stdplane(nc);
  ncplane_dim_yx(plane, &dimy, &dimx);
  int r , g, b;
  r = 0;
  g = 0x80;
  b = 0;
  ncplane_set_bg_rgb8(plane, 0x40, 0x20, 0x40);
  for(unsigned y = 0 ; y < dimy ; ++y){
    for(unsigned x = 0 ; x < dimx ; ++x){
      if(ncplane_set_fg_rgb8(plane, r, g, b)){
        goto err;
      }
      if(ncplane_cursor_move_yx(plane, y, x)){
        goto err;
      }
      if(ncplane_putchar(plane, 'x') <= 0){
        goto err;
      }
      if(g % 2){
        if(--b <= 0){
          ++g;
          b = 0;
        }
      }else{
        if(++b >= 256){
          ++g;
          b = 255;
        }
      }
    }
  }

  struct cellcopyarray   array;

  cellcopyarray_init_with_plane( &array, plane);
  if( ! array.cells)
    return EXIT_FAILURE;

  ncplane_erase( plane);
  cellcopyarray_write_to_plane( &array, plane);
  cellcopyarray_done( &array);

  if(notcurses_render(nc)){
    notcurses_stop(nc);
    return EXIT_FAILURE;
  }
  notcurses_stop(nc);
  return EXIT_SUCCESS;

err:
  notcurses_stop(nc);
  return EXIT_FAILURE;
}

mulle-nat avatar Feb 19 '24 14:02 mulle-nat