
// SPDX-License-Identifier: CC-BY-NC-SA-4.0
//
// Copyright (C) 2026 Bit by Bit Signal Processing LLC (https://bxbsp.com)
//
// This work is placed under the "Creative Commons Attribution
// NonCommercial ShareAlike 4.0 International" license, known
// by the shortened acronym "CC-BY-NC-SA-4.0".
//
// This work is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// A CC-BY-NC-SA-4.0 license allows you to use this work for
// noncommercial purposes so long as attribution is made to the
// original author.  Modified versions of this work may be distributed,
// but only under the same license.  For further details, see the
// Creative Commons License "CC-BY-NC-SA-4.0".
//
// You should have received a copy of the CC-BY-NC-SA-4.0 license
// along with this work. If not, see
// <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
//

#include "display.hh"



#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#define virtual virtualm
#include <libdrm/drm.h>
#undef virtual

#include <libdrm/drm_mode.h>

#include "displays.hh"
#include "color.hh"

//void create_display_panels();
//#define run_at_start(function) class at_start { public: at_start(void (*func)()) { func(); }; } run(function)
//run_at_start(create_display_panels);


void display::trash_events_until_release()
{
  //release_all_events();
  //printf("trash_events_until_release called.\n");
  trash_events_flag = true;
}


//
// Setting the parent to null is OK, because no reference to the parent should every come out
// of a display. All the functions that reference a parent are overridden.
//
display::display(int w, int h, int num_panes)
  : multiwindow_multipane(BLACK, num_panes)
{
  resize(0,0,w,h);
  trash_events_flag  = false;
  us_to_trash_events = 0;
}


display::~display()
{
}



void display::trash_events_for_us(long microseconds)
{
  us_to_trash_events = microseconds;
  gettimeofday(&time_to_start_trashing_events, 0);
}


bool display::handle_event(my_event& me)
{
  bool handled = false;

  if(us_to_trash_events)
    {
      struct timeval t;
      gettimeofday(&t, 0);

      long long t0_us = time_to_start_trashing_events.tv_sec*1000000ll + time_to_start_trashing_events.tv_usec;
      long long t1_us = t.tv_sec*1000000ll + t.tv_usec;

      long long elapsed_us = t1_us - t0_us;

      //printf("us_to_trash_events elapsed time is %lld\n", elapsed_us);
      
      if(elapsed_us > (long long) us_to_trash_events)
	us_to_trash_events = 0;
    }

  //printf("us_to_trash_events=%ld\n", us_to_trash_events);
  
  if(us_to_trash_events)
    return true;
  
  //printf("in handle_event for %p.\n", this);
  
  for(ListElement<window>* le=priority_event_listeners.last(); le; )
    {
      window* w = le->data();
      le=le->previous();  // Get the next one here, because le might be deleted by handle_event since it may cause an item to remove itself from the list.
      
      //printf("handling priority events for window %p\n", w);

      handled = w->handle_event(me);

      //printf("Priority event listenter returned %d\n", handled);
      
      if(handled)
	break;
    }

  //printf("Done with priorty event listeners.  handled=%d\n", handled);
  
  if(!handled)
    {
      ListElement<window>* le=subwindows->last();

      //printf("got here a\n");
      
      // If we have no subwindows, just return errors silently.
      if(le==0)
	return true;
  
      handled = multiwindow::handle_event(me);
    }
  
  //printf("got here b, handled=%d\n", handled);
  return handled;
}


void display::claim_events(window* w)
{
  //printf("claiming events for window %p\n", w);

  if(!priority_event_listeners.isInList(w))
    {
      priority_event_listeners.addAtEnd(w, false);
    }
}


void display::release_events(window* w)
{
  //printf("releasing events for window %p\n", w);

  if(w==0)
    abort();
  
  for(ListElement<window>* le=priority_event_listeners.last(); le; )
    {
      ListElement<window>* this_le = le;
      le=le->previous();
      window* w2 = this_le->data();

      if(w == w2)
	{
	  priority_event_listeners.remove(this_le);
	  release_events(w);  // make sure it's not on the list again.  Go back through from the start, since we just messed up the list.
	  break;
	}
    }
}


void display::release_all_events()
{
  //printf("In release_all_events.\n");
  
  for(ListElement<window>* le=priority_event_listeners.last(); le; )
    {
      ListElement<window>* this_le = le;
      le=le->previous();
      priority_event_listeners.remove(this_le);
    }
  //printf("Leaving release_all_events.\n");
}


void display::pane_change()
{
  release_all_events();
}


class display* display::get_display()
{
  return this;
}

