Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API feels gimped, need ncplanes without parents #2761

Open
mulle-nat opened this issue Feb 19, 2024 · 2 comments
Open

API feels gimped, need ncplanes without parents #2761

mulle-nat opened this issue Feb 19, 2024 · 2 comments
Assignees
Labels
documentation Improvements or additions to documentation enhancement New feature or request
Milestone

Comments

@mulle-nat
Copy link
Contributor

mulle-nat commented Feb 19, 2024

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 😉 .

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 mulle-nat added documentation Improvements or additions to documentation enhancement New feature or request labels Feb 19, 2024
@dankamongmen dankamongmen added this to the 4.0.0 milestone Feb 20, 2024
@dankamongmen
Copy link
Owner

sooooooooooo i'm currently thinking about what all ought happen for 4.0, and i'll look into this for that scope.

@dankamongmen dankamongmen self-assigned this Feb 20, 2024
@mulle-nat
Copy link
Contributor Author

One more suggestion for 4.0.0: It would be nice to have ncplane_cell_ref_yx exposed, because the checking code piles up over the two dimensional looping. Since ncplane_at_yx_cell is not inline, an optimizing compiler can not hoist the checks out of the loop. With ncplane_cell_ref_yx exposed, you could turn ncplane_at_yx_cell into an inline function with basically zero overhead compare to ncplane_cell_ref_yx.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants