bobe001
Posts: 10
Joined: Wed Dec 09, 2015 11:01 pm

Pigpio and GTK

Sat Jan 26, 2019 10:51 pm

Hi

I'm trying to use pigpio to call an interrupt when a raspberry pi gpio pin changes state which in turn will call the GTK widget draw function.

I can create the pigpio function but if I try to call gtk_widget_queue_draw using a global defined widget the program instantly hangs. Does anyone know what I'm doing wrong?

the callback is very simply

void MicCallback (int Pin, int Edge, uint32_t tick)
{
gtk_widget_queue_draw(Info.widget);
}
where info.widget is defined as
info.widget = gtk_application_window_new(app);

Bob.

User avatar
PeterO
Posts: 5623
Joined: Sun Jul 22, 2012 4:14 pm

Re: Pigpio and GTK

Sun Jan 27, 2019 10:14 am

Probably not a good idea to try and call gtk functions from inside that handler. There could be threading issues ?

I would just set a semaphore of some kind in the handler, and then poll the flag from the "Main Event Loop" by setting up a new event source.

Look at https://developer.gnome.org/glib/stable ... -Loop.html for details of how to use the Main Event Loop,

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

bobe001
Posts: 10
Joined: Wed Dec 09, 2015 11:01 pm

Re: Pigpio and GTK

Sun Jan 27, 2019 9:39 pm

Thanks Peter, That does seem to be a good way out of the problem. You will have to excuse me for being thick - being very new to GTK being my only excuse, but none of the functions in your link seem to me to be capable of creating an event and callback, from, say, a global integer. Is there any examples anywhere of how this could be achieved? I've tried googling it but seem to be drawing blanks

Bob

User avatar
PeterO
Posts: 5623
Joined: Sun Jul 22, 2012 4:14 pm

Re: Pigpio and GTK

Sun Jan 27, 2019 10:43 pm

bobe001 wrote:
Sun Jan 27, 2019 9:39 pm
Thanks Peter, That does seem to be a good way out of the problem. You will have to excuse me for being thick - being very new to GTK being my only excuse, but none of the functions in your link seem to me to be capable of creating an event and callback, from, say, a global integer. Is there any examples anywhere of how this could be achieved? I've tried googling it but seem to be drawing blanks

Bob
If you can wait until tomorrow I'll have a go at producing an example for you , but it's a bit late here now to start now ....
PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

bobe001
Posts: 10
Joined: Wed Dec 09, 2015 11:01 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 9:25 am

Thanks Peter, I appreciate your help in deciphering the mysteries of GTK

Bob

User avatar
PeterO
Posts: 5623
Joined: Sun Jul 22, 2012 4:14 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 10:14 am

bobe001 wrote:
Mon Jan 28, 2019 9:25 am
Thanks Peter, I appreciate your help in deciphering the mysteries of GTK

Bob
Working on some code for you right now :-)
I think it's worth the effort to get the hang of GTK+.
I hope you are using version 3!

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

User avatar
PeterO
Posts: 5623
Joined: Sun Jul 22, 2012 4:14 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 11:31 am

Before setting up the main loop callbacks I first tried just calling gtk_widget_queue_draw() in the gpio callback and it seems to work OK ! I don't know why your code didn't work or why my code does :lol: .

Compile with this

Code: Select all

gcc -Wall -pthread -o gtkgpio gtkgpio.c.c $(pkg-config --cflags --libs gtk+-3.0) -lpigpio -lrt -rdynamic

gtkgpio.c

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pigpio.h>

#include <gtk/gtk.h>

#define TESTPIN 18

// Prototypes for gtk event handlers

gboolean
on_quitButton_clicked(__attribute__((unused)) GtkButton *widget,
		       __attribute__((unused)) gpointer data);

gboolean
on_onButton_clicked(__attribute__((unused)) GtkButton *widget,
		       __attribute__((unused)) gpointer data);

gboolean
on_offButton_clicked(__attribute__((unused)) GtkButton *widget,
		       __attribute__((unused)) gpointer data);

gboolean
on_drawingArea_draw(__attribute__((unused)) GtkWidget *widget,
                    cairo_t *cr,
                    __attribute__((unused)) gpointer user_data);

static gboolean toggleLed(__attribute__((unused)) gpointer user_data);

void Callback(int gpio, int level, uint32_t tick);

static GtkWidget *drawingArea;

static int drawingAreaWidth,drawingAreaHeight;         

int main(int argc,char **argv)
{
    static GtkBuilder *builder = NULL;

    gtk_init(&argc,&argv);
    
    // Load widgets from glade file
    builder = gtk_builder_new();

    if( gtk_builder_add_from_file (builder,"gtkgpio.glade", NULL) == 0)
    {
        printf("gtk_builder_add_from_file() FAILED\n");
	exit(0);
    }

    // Get pointer to the drawing area widget
    drawingArea = GTK_WIDGET (gtk_builder_get_object (builder,"drawingArea"));

    drawingAreaWidth = gtk_widget_get_allocated_width(drawingArea);
    drawingAreaHeight = gtk_widget_get_allocated_height(drawingArea);

    gtk_builder_connect_signals(builder,NULL);
    
    if (gpioInitialise() < 0)
    {
	// pigpio initialisation failed.
	printf("gpioInitialise failed\n");
    }
    else
    {
	// pigpio initialised okay.
	printf("gpioInitialise OK\n");
	gpioSetMode(TESTPIN, PI_OUTPUT); // Set GPIO TESTPIN as output.
	
	gpioSetISRFunc(TESTPIN, RISING_EDGE, 2000, Callback);
	// Note the callback will be called every 2 seconds if the pin isn't chaged.

	// Uncomment next lne for automatic toggling of pin
	//g_timeout_add(100,toggleLed,NULL);

	gtk_main();
    }

    gpioTerminate();
}

gboolean
on_quitButton_clicked(__attribute__((unused)) GtkButton *widget,
		       __attribute__((unused)) gpointer data)
{
    gtk_main_quit();

    return TRUE;
}

gboolean
on_onButton_clicked(__attribute__((unused)) GtkButton *widget,
		       __attribute__((unused)) gpointer data)
{
    gpioWrite(TESTPIN, TRUE);
    
    return TRUE;
}

gboolean
on_offButton_clicked(__attribute__((unused)) GtkButton *widget,
		       __attribute__((unused)) gpointer data)
{
    gpioWrite(TESTPIN, FALSE);
    
    return TRUE;
}


// timeout handler to falsh the LED
__attribute__((used))
static gboolean toggleLed(__attribute__((unused)) gpointer user_data)
{
    static gboolean on = FALSE;

    on = !on;
    gpioWrite(TESTPIN, on);
    
    return TRUE;
}


// gpio change callback
void Callback(int gpio, int level, uint32_t tick)
{
    // Draw the drawing area
    gtk_widget_queue_draw(drawingArea);

    printf("Callback called at %u\n",tick);
}

// Draw a "pie chart"
gboolean
on_drawingArea_draw(__attribute__((unused)) GtkWidget *widget,
                    cairo_t *cr,
                    __attribute__((unused)) gpointer user_data)
{
    static gdouble angle = 0.0;

    angle += 0.1;
    cairo_set_source_rgba(cr,0.5,0.5,0.5,1.0);

    cairo_move_to (cr,0.5*drawingAreaWidth,0.5*drawingAreaHeight);
    cairo_arc(cr,0.5*drawingAreaWidth,0.5*drawingAreaHeight,50.0,0.0,angle);
    cairo_line_to (cr,0.5*drawingAreaWidth,0.5*drawingAreaHeight);
    
    cairo_fill(cr);
    
    return FALSE;
}
gtkgpio.glade

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkDrawingArea" id="drawingArea">
            <property name="width_request">200</property>
            <property name="height_request">200</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <signal name="draw" handler="on_drawingArea_draw" swapped="no"/>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkButtonBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="layout_style">start</property>
            <child>
              <object class="GtkButton" id="onButton">
                <property name="label" translatable="yes">ON</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="on_onButton_clicked" swapped="no"/>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="offButton">
                <property name="label" translatable="yes">OFF</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="on_offButton_clicked" swapped="no"/>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="quitButton">
                <property name="label">gtk-quit</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
                <property name="always_show_image">True</property>
                <signal name="clicked" handler="on_quitButton_clicked" swapped="no"/>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">2</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

User avatar
PeterO
Posts: 5623
Joined: Sun Jul 22, 2012 4:14 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 12:13 pm

Also, you might find looking at the source code for piscope to be useful.
http://abyz.me.uk/rpi/pigpio/piscope.html
See "All machines (building from source)" at the bottom of the page.

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

bobe001
Posts: 10
Joined: Wed Dec 09, 2015 11:01 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 12:30 pm

Hi peter, Thanks for this, Yes I am using 3. Interesting that your code calls gtk_widget_queue_draw() properly, the first think I can see is you are using gpioSetISRFunc() while I use gpioSetAlertFunc(); maybe that's where the problem lies.. I will test that one out.. Also I notice you compile with -rdynamic which I don't use.. I'll investigate that as well..


Just as an aside my non working code looks like this, is there anything really stupid happening in there?

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
int main (int argc, char **argv)
{
GtkApplication *app;
char buf[255] = "";
char *dest;
int status;
int x;

GetCurrentDirectory(ProgDir,sizeof(ProgDir)-1);

for (int i = 0; i < argc;++i)
{
if (strcasecmp(argv,"-ah") == 0)
{
AlternativeClockHands = TRUE;
}
else if (strcasecmp(argv,"-showtimer") == 0)
{
ForceShowTiming = ON;
}
else if (strcasecmp(argv,"-onair=on") == 0)
{
ForceOnAirMode = ON;
}
else if (strcasecmp(argv,"-onair=off") == 0)
{
ForceOnAirMode = OFF;
}
else if (strcasecmp(argv,"-mic=on") == 0)
{
ForceMicMode = ON;
}
else if (strcasecmp(argv,"-mic=off") == 0)
{
ForceMicMode = OFF;
}
else if (strcasecmp(argv,"-phone1=on") == 0)
{
ForcePhone1Mode = ON;
}
else if (strcasecmp(argv,"-phone1=off") == 0)
{
ForcePhone1Mode = OFF;
}
else if (strcasecmp(argv,"-phone2=on") == 0)
{
ForcePhone2Mode = ON;
}
else if (strcasecmp(argv,"-phone2=off") == 0)
{
ForcePhone2Mode = OFF;
}
else if (strncasecmp(argv[i],"-name=",6) == 0)
{
x = i; // Clone arg number
dest = (char *)argv[x]+6; // Point Over The -name= bit
sprintf(ProgName,"%s",dest); // Poke rest into the buffer
for (++x; x < argc; x++) // Then look for suceeding argv's
{
if (strncmp(argv[x],"-",1) == 0) // That don't contain a "-"
{
break; // If they do crash - we've found the next argv
}
else
{
strcat(ProgName," ");
strcat(ProgName,argv[x]);
}
}
}
}

argc = 0;
if (gpioInitialise() < 0)
{
printf("Pigpio failed to Start\n");
}
//GPIOInit();
// Default To Get Us Started
sprintf(buf,"%shcr.png",ProgDir);
glob.image = cairo_image_surface_create_from_png(buf);
sprintf(buf,"%shcrPhoneRed.png",ProgDir);
glob.phimage = cairo_image_surface_create_from_png(buf);

app = gtk_application_new ("Bob.Elliot.Studio.Clock", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
////////////////////////////////////////////////////////////////////////
////////////////////// On Program Termination //////////////////////////
gpioTerminate();
cairo_surface_destroy(glob.image);
cairo_surface_destroy(glob.phimage);
g_object_unref (app);
return status;
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void GPIOInit(GtkWidget *window)
{
// Set Input Ports
gpioSetMode(OnAirIn, PI_INPUT);
gpioSetMode(MicIn, PI_INPUT);
gpioSetMode(Phone1In, PI_INPUT);
gpioSetMode(Phone2In, PI_INPUT);
// Set Output Ports
gpioSetMode(OnAirOut, PI_OUTPUT);
gpioSetMode(MicOut, PI_OUTPUT);
gpioSetMode(Phone1Out, PI_OUTPUT);
gpioSetMode(Phone2Out, PI_OUTPUT);
// Set Pullup Resistors On Inputs
gpioSetPullUpDown(OnAirIn, PI_PUD_UP);
gpioSetPullUpDown(MicIn, PI_PUD_UP);
gpioSetPullUpDown(Phone1In, PI_PUD_UP);
gpioSetPullUpDown(Phone2In, PI_PUD_UP);
// Set Glitch Filters
gpioGlitchFilter(OnAirIn, 300);
gpioGlitchFilter(MicIn, 300);
gpioGlitchFilter(Phone1In, 300);
gpioGlitchFilter(Phone2In, 300);

gpioSetAlertFunc(OnAirIn,GPIOCallBack);
gpioSetAlertFunc(MicIn,GPIOCallBack);
gpioSetAlertFunc(Phone1In,GPIOCallBack);
gpioSetAlertFunc(Phone2In,GPIOCallBack);

DispInfo.LastOnAirState = -1;
DispInfo.LastMicState = -1;
DispInfo.LastPh1State = -1;
DispInfo.LastPh2State = -1;

}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void GPIOCallBack(int Pin,int Edge,uint32_t tick)
{
switch (Pin)
{
case OnAirIn:
DispInfo.OnAirState=(ForceOnAirMode==-1)? Edge:ForceOnAirMode;
break;
case MicIn:
DispInfo.MicState=(ForceMicMode==-1)? Edge:ForceMicMode;
break;
case Phone1In:
DispInfo.Ph1State=(ForcePhone1Mode==-1)? Edge:ForcePhone1Mode;
break;
case Phone2In:
DispInfo.Ph2State=(ForcePhone2Mode==-1)? Edge:ForcePhone2Mode;
}
//printf("In CallBack Pin %d edge = %d\n",Pin,Edge);
}

The States are then polled by the g_timeout_add() function (which I hate when servicing the original interrupt is an option)
g_timeout_add (50, (GSourceFunc) display_handler);

////////////////////////////////////////////////////////////////////////
// Here We Look At At Handle any changes in the studio light display //
////////////////////////////////////////////////////////////////////////
static gint display_handler (GtkWidget *widget)
{
double h,x,y;

getscreenres(&DispInfo,TRUE);
h = DispboxHeight;
x = DispInfo.midx *2;

if (DispInfo.OnAirState != DispInfo.LastOnAirState || DispInfo.ResChange == TRUE)
{
y = (DispAreaTop + guardHeight);
gtk_widget_queue_draw_area(widget,x,y,x,h * 1.01);
gpioWrite(OnAirOut,DispInfo.OnAirState);
}

if (DispInfo.MicState != DispInfo.LastMicState || DispInfo.ResChange == TRUE )
{
y = DispAreaTop + (DispboxHeight + (guardHeight*2));
gtk_widget_queue_draw_area(widget,x,y,x,h * 1.01);
gpioWrite(MicOut,DispInfo.MicState);

}

if (DispInfo.Ph1State != DispInfo.LastPh1State || DispInfo.ResChange == TRUE )
{
y = DispAreaTop + ((DispboxHeight * 2) + (guardHeight * 3));
gtk_widget_queue_draw_area(widget,x,y,x,h * 1.01);
gpioWrite(Phone1Out,DispInfo.Ph1State);
}

if (DispInfo.Ph2State != DispInfo.LastPh2State || DispInfo.ResChange == TRUE )
{
y = DispAreaTop + ((DispboxHeight * 3) + (guardHeight * 4));
gtk_widget_queue_draw_area(widget,x,y,x,h * 1.01);
gpioWrite(Phone2Out,DispInfo.Ph2State);
}

return(TRUE);
}

User avatar
PeterO
Posts: 5623
Joined: Sun Jul 22, 2012 4:14 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 12:51 pm

Here is the version that uses the event loop and an event source.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pigpio.h>
#include <math.h>

#include <gtk/gtk.h>


#define TESTPIN 18

// Prototypes for gtk event handlers

gboolean
on_quitButton_clicked(__attribute__((unused)) GtkButton *widget,
		       __attribute__((unused)) gpointer data);

gboolean
on_onButton_clicked(__attribute__((unused)) GtkButton *widget,
		       __attribute__((unused)) gpointer data);

gboolean
on_offButton_clicked(__attribute__((unused)) GtkButton *widget,
		       __attribute__((unused)) gpointer data);


gboolean
on_drawingArea_draw(__attribute__((unused)) GtkWidget *widget,
                    cairo_t *cr,
                    __attribute__((unused)) gpointer user_data);



static gboolean toggleLed(__attribute__((unused)) gpointer user_data);

void Callback(int gpio, int level, uint32_t tick);


static GtkWidget *drawingArea;

static GSourceFuncs GpioSourceFuncs;
static gboolean GpioCallback(gpointer user_data);
static gboolean bitChanged = FALSE;

static int drawingAreaWidth,drawingAreaHeight;         

int main(int argc,char **argv)
{

    static GtkBuilder *builder = NULL;
    GSource *GpioSource = NULL;


    gtk_init(&argc,&argv);
    
    
    // Load widgets from glade file
    builder = gtk_builder_new();

    if( gtk_builder_add_from_file (builder,"gtkgpio.glade", NULL) == 0)
    {
        printf("gtk_builder_add_from_file() FAILED\n");
	exit(0);
    }

    // Get pointer to the drawing area widget
    drawingArea = GTK_WIDGET (gtk_builder_get_object (builder,"drawingArea"));

    drawingAreaWidth = gtk_widget_get_allocated_width(drawingArea);
    drawingAreaHeight = gtk_widget_get_allocated_height(drawingArea);

    

    gtk_builder_connect_signals(builder,NULL);

    
    if (gpioInitialise() < 0)
    {
	// pigpio initialisation failed.
	printf("gpioInitialise failed\n");
	
    }
    else
    {
	// pigpio initialised okay.
	printf("gpioInitialise OK\n");
	gpioSetMode(TESTPIN, PI_OUTPUT); // Set GPIO TESTPIN as output.
	
	
	gpioSetISRFunc(TESTPIN, RISING_EDGE, 2000, Callback);
	// Note the callback will be called every 2 seconds if the pin isn't chaged.

	// Uncomment next lne for automatic toggling of pin
	g_timeout_add(100,toggleLed,NULL);



	GpioSource = g_source_new(&GpioSourceFuncs,sizeof(GSource));

	g_source_attach(GpioSource,NULL);
	g_source_set_callback(GpioSource,GpioCallback,NULL,NULL);
                                                         
	g_source_set_priority(GpioSource,G_PRIORITY_DEFAULT);


	gtk_main();
	
      
    }


    gpioTerminate();

}

gboolean
on_quitButton_clicked(__attribute__((unused)) GtkButton *widget,
		       __attribute__((unused)) gpointer data)
{
    gtk_main_quit();

    return TRUE;
}


gboolean
on_onButton_clicked(__attribute__((unused)) GtkButton *widget,
		       __attribute__((unused)) gpointer data)
{
    gpioWrite(TESTPIN, TRUE);
    
    return TRUE;

}
gboolean
on_offButton_clicked(__attribute__((unused)) GtkButton *widget,
		       __attribute__((unused)) gpointer data)
{
    gpioWrite(TESTPIN, FALSE);
    
    return TRUE;

}


// timeout handler to falsh the LED
__attribute__((used))
static gboolean toggleLed(__attribute__((unused)) gpointer user_data)
{
    static gboolean on = FALSE;

    on = !on;
    gpioWrite(TESTPIN, on);
    
    return TRUE;
}


// gpio change callback
void Callback(int gpio, int level, uint32_t tick)
{
    bitChanged = TRUE;

    //printf("Callback called at %u\n",tick);
}



// Draw a "pie chart"
gboolean
on_drawingArea_draw(__attribute__((unused)) GtkWidget *widget,
                    cairo_t *cr,
                    __attribute__((unused)) gpointer user_data)
{
    static gdouble angle = 0.0;

    angle += 0.1;
    cairo_set_source_rgba(cr,0.5,0.5,0.5,1.0);

    cairo_move_to (cr,0.5*drawingAreaWidth,0.5*drawingAreaHeight);
    cairo_arc(cr,0.5*drawingAreaWidth,0.5*drawingAreaHeight,50.0,0.0,angle);
    cairo_line_to (cr,0.5*drawingAreaWidth,0.5*drawingAreaHeight);
    
    cairo_fill(cr);

    if(angle >= 2.0*M_PI) angle = 0.0;
    
    return FALSE;
}


gboolean  GpioCallback(gpointer user_data)
{
    gtk_widget_queue_draw(drawingArea);
    return TRUE;
}


static gboolean GpioPrepareFunc(__attribute__((unused)) GSource *source, gint *timeout)
{
    *timeout = 100;   // Don't let the poll in the main loop block for more than 100mS
    if(bitChanged == TRUE)
    {
	bitChanged = FALSE;
	return TRUE;   //  This will cause the dispatch function to be called
    }
    else
	return FALSE;
}

static gboolean GpioDispatchFunc(GSource *source,
                                 __attribute__((unused)) GSourceFunc cb, 
                                 __attribute__((unused))gpointer data)
{
    // All we do here is call the specified callback
    (*cb)(data);
    
    return TRUE;
}

static GSourceFuncs GpioSourceFuncs = {
    GpioPrepareFunc,
    NULL, 
    GpioDispatchFunc,
    NULL,
    NULL,
    NULL
};
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

User avatar
PeterO
Posts: 5623
Joined: Sun Jul 22, 2012 4:14 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 1:02 pm

bobe001 wrote:
Mon Jan 28, 2019 12:30 pm
Hi peter, Thanks for this, Yes I am using 3. Interesting that your code calls gtk_widget_queue_draw() properly, the first think I can see is you are using gpioSetISRFunc() while I use gpioSetAlertFunc(); maybe that's where the problem lies.
You didn't say which one you were using so I had to guess :-)
. I will test that one out.. Also I notice you compile with -rdynamic which I don't use.. I'll investigate that as well..
That's needed so that "gtk_builder_connect_signals(builder,NULL);" can find the functions that match the event handlers specified in the glade file.
Just as an aside my non working code looks like this, is there anything really stupid happening in there?
You've not supplied a complete program (e.g. missing header files and #includes) so I can't test it to see what's wrong (Lots of missing symbols when I tried to compile it).

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

incognitum
Posts: 452
Joined: Tue Oct 30, 2018 3:34 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 2:21 pm

May also want to try playing with idle functions.

Code: Select all

gboolean redrawThings(gpointer user_data)
{
   gtk_widget_queue_draw (Info.widget);
   return FALSE; /* Only run once */
}

void MicCallback (int Pin, int Edge, uint32_t tick)
{
   /* Schedule redrawThings to be executed in main event loop */
   g_idle_add (redrawThings, 0);
}
Last edited by incognitum on Mon Jan 28, 2019 2:34 pm, edited 1 time in total.

bobe001
Posts: 10
Joined: Wed Dec 09, 2015 11:01 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 2:34 pm

Hi Peter

The whole program is quite large and requires .pngs to work - is there a way I can send them to you? It would be really useful to have a pair of experienced eyes looking at it.

I've tried your way of using the pigpio callback but I still get screen freeze after a couple of clicks - or occasionally the Gdk error: Fatal IO error 11 (resource Temporarily Unavailable) on X-server :0..0.
I think I will try a strip down window to ensure I've not done something stupid elsewhere that's affecting this callback function.

I can attach the (edited non working version of the program) - the pngs - those can always be commented out..

static const char version[] = "8.0.0B";

#define _GNU_SOURCE
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <cairo.h>
#include <gtk/gtk.h>
#include <time.h>
#include <pigpio.h>

///////////////////////// DEFINITIONS //////////////////////////////////

#define WHITE 1.000, 1.000, 1.000
#define DARKRED 0.770, 0.160, 0.130
#define RED 1.000, 0.000, 0.000
#define TOMATO 1.000, 0.388, 0.278
#define YELLOW 1.000, 1.000, 0.000
#define GREEN 0.000, 1.000, 0.129
#define SKYBLUE 0.530, 0.800, 0.980
#define PALECORNFLOWER 0.700, 0.965, 1.000
#define BLUE 0.000, 0.000, 1.000
#define MAGENTA 1.000, 0.000, 1.000
#define DARKGRAY 0.050, 0.050, 0.050
#define BLACK 0.000, 0.000, 0.000

//////////////////// GPIO PIN BROADCOM DEFINITIONS /////////////////////
#define MicIn (4) // Broadcom GPIO 04 >> Pin 07
#define MicOut (22) // Broadcom GPIO 22 >> Pin 15

#define OnAirIn (24) // Broadcom GPIO 24 >> Pin 18
#define OnAirOut (18) // Broadcom GPIO 18 >> Pin 12

#define Phone1In (25) // Broadcom GPIO 25 >> Pin 22
#define Phone1Out (27) // Broadcom GPIO 27 >> Pin 13

#define Phone2In (23) // Broadcom GPIO 23 >> Pin 16
#define Phone2Out (17) // Broadcom GPIO 17 >> Pin 11

#define OFF (0)
#define ON (1)

#define HandsColor BLUE
#define ClockfaceColor PALECORNFLOWER //SKYBLUE

#define TextBoxPercent 10.0
#define HGuardBoxPercent 1.0
#define DispBoxPercent (100.0 - ((TextBoxPercent * 3.0) + (HGuardBoxPercent * 4)))

static int TimeDatePointSize = 90; //85;
static int MessagePointSize = 40;

typedef struct
{
float x;
float y;
}POS;

typedef struct
{
GtkWidget *clockArea; // Clock Face Drawing Area Handle
GtkWidget *dateArea; // Day And Date Drawing Area
GtkWidget *timeArea; // Time Drawing Area
GtkWidget *window; // Main Window Handle
int ResChange; // Screen Refresh Needed
int DispWidth; // Physical Screen Width
int DispHeight; // Physical Screen Height
int WorkWidth; // Real Screen Area (Decorations subtracted)
int DecorationHeight; // Size of Menu bar in Pixels
double GuardWidth; // Blank space around Display Boxes
double deltaHeight; // Work area delta height when decorated
int midx; // Mid Width Of Screen
int midy; // Mid Height Point of Screen
double w; // Width of the Display Box
int InhibitDispDraw; // Disable Drawing Of Boxes Till We Have Sizes
int LastOnAirState; // Saved On Air State
int OnAirState; // Signaled On Air State
int LastMicState;
int MicState;
int LastPh1State;
int Ph1State;
int LastPh2State;
int Ph2State;
}DISPINFO;

struct
{
cairo_surface_t *image;
cairo_surface_t *phimage;
}glob;

static struct tm *loctime;
static int seconds;
static int last_seconds = 99;
static int minutes;
static int last_minutes = 99;
static int hours;
static int doy;
static int last_doy = 999;
static int dow;
static int mday;
static int month;
static int year;
static int trip = 0;

DISPINFO DispInfo = {0};

static int AlternativeClockHands = FALSE;
static int ForceShowTiming = FALSE;
static int ForceOnAirMode = -1;
static int ForceMicMode = -1;
static int ForcePhone1Mode = -1;
static int ForcePhone2Mode = -1;
static int ScreenWidth;
static int ScreenHeight;

static int YearDay = -1; // Illegal day To Ensure Correct Start
static char ProgName[255] = ""; // The Name Displayed On The Decoration
static char ProgDir[255] = "";
static const char *Months[] = { "January","February","March","April",
"May","June","July","August","September",
"October","November","December"};
static const char *Days[] = { "Sunday","Monday","Tuesday","Wednesday",
"Thursday","Friday","Saturday"};

static double WorkHeight; // work area height(normalised for decoration)
static double DispboxHeight; // Height of single Display Box
static double guardHeight; // Height of Guard Area Between Disp Boxes
static double TextboxHeight; // Text Area (Single Line)
static double DispAreaTop; // Top Of Disp Box Area
static double DispAreaBottom; // Bottom Of Disp Box Area

////////////////////////////////////////////////////////////////////////
// Function Declarations
////////////////////////////////////////////////////////////////////////
void DrawBurnsNightText(cairo_t *cr, int cx);
void DrawClockChangeText(cairo_t *cr, int cx, int day);
void DrawClockFace (cairo_t *, int, double);
void DrawCopyrightText(cairo_t *cr, int cx);
void DrawDateText(cairo_t *);
void DrawDayText(cairo_t *);
void DrawHourHand (cairo_t *, int, double);
void DrawImage(cairo_t *,int,cairo_surface_t *);
void DrawMessageBox(cairo_t *cr,int cx,int tp,int Today,int Year);
void DrawMinuteHand (cairo_t *cr, int cx, double cy);
void DrawNumbers(cairo_t *,int , double );
void DrawSecondHand (cairo_t *cr, int cx, double cy);
void DrawTestMessage(cairo_t *cr, int cx);
void DrawTicks (cairo_t *cr, int cx, double cy);
void DrawTimeText(cairo_t *, int );
void DrawXmasText(cairo_t *cr, int cx, int count);
void getscreenres (DISPINFO *Info,int Refresh);
void GPIOInit(GtkWidget *window);
void wiringPiInit(void);
void SelectCorrectLogo(cairo_surface_t *image);
int CalculateClockChangeDay(int Today,int Year);
int CalculateEasterSunday(int );
int CalculateXmasCountdown(int Today,int Year);
int GetCurrentDirectory(char *pBuff,int Len);
int GetYearDay(int , int , int );
int yisleap(int );
POS ComputePos(float ,float ,float , float );


///////////// CalculateGoTCountdown(int Today, int year) //////////////
// Calculates The Number of Days Left Till Game Of Thrones 2019 Day.
////////////////////////////////////////////////////////////////////////
int CalculateGoTCountdown(int Today)
{
int Days = -1;
int GoTDay = GetYearDay(15,4,2019);
Days = GoTDay - Today;
if (Days >= 0)
return(Days);
else
return(-1);
}


////////////////////////////////////////////////////////////////////////
// Prints a countdown of the number of days left till GOT //
// Ends On 14 April 2019 With An appropriate Message //
////////////////////////////////////////////////////////////////////////
void DrawGoTText(cairo_t *cr, int cx, int count)
{
char Str[40] = "";
cairo_text_extents_t extents;
cairo_font_extents_t fextents;

//count = 0;
cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size (cr, MessagePointSize);
cairo_font_extents(cr, &fextents);

switch(count)
{
case 0: // It's GOT Day
{
cairo_set_source_rgb(cr,YELLOW);
strcpy(Str,"It's Time For Game Of Thrones!!");
cairo_text_extents(cr,Str, &extents);
cairo_move_to(cr, (cx - (extents.width)/2),fextents.ascent);
cairo_show_text(cr,Str);
break;
}
default: // Countdown To Xmas day
{
cairo_set_source_rgb(cr,YELLOW);
sprintf(Str,"%s%d more %s till Game Of Thrones",
(count <= 5)? "Only ": "",count,
(count > 1)? "days": "day");
cairo_text_extents(cr,Str, &extents);
cairo_move_to(cr, (cx - (extents.width)/2),fextents.ascent);
cairo_show_text(cr,Str);
}
}
}

////////////////////////////////////////////////////////////////////////
// Retuns The Width and Height of the connected monitor //
////////////////////////////////////////////////////////////////////////
void getscreenres (DISPINFO *Info,int Refresh)
{
double DecorationHeight;
GdkRectangle r,w;
GdkDisplay * display = gdk_display_get_default();
GdkMonitor * monitor = gdk_display_get_monitor(display, 0);

gdk_monitor_get_geometry(monitor, &r);
gdk_monitor_get_workarea(monitor,&w);

Info->DispWidth = r.width;
Info->DispHeight = r.height;
Info->WorkWidth = w.width;
//Info->WorkHeight = w.height;
DecorationHeight = r.height * 0.028; //r.height - w.height;
//printf("decoration height %f\n",DecorationHeight);
if (gtk_window_get_decorated(GTK_WINDOW (DispInfo.window)) == TRUE)
{
Info->deltaHeight = DecorationHeight;
}
else
{
Info->deltaHeight = 0.0;
}
WorkHeight = w.height - Info->deltaHeight;

Info->midx = r.width/4;
Info->midy = (r.height/2) - (Info->deltaHeight/2);

double DispArea = (WorkHeight * (100 - (TextBoxPercent *3))) / 100;
TextboxHeight = (WorkHeight * TextBoxPercent) / 100.0; // Text Box = 10% of Height
DispAreaTop = TextboxHeight * 2.0;
DispAreaBottom = WorkHeight - TextboxHeight;

guardHeight = (WorkHeight * HGuardBoxPercent) / 100.0; // H guard = 2% of Height
DispboxHeight = ((WorkHeight * DispBoxPercent) /100) / 4.0; // 4 Boxes
if (Refresh == TRUE)
DispInfo.ResChange = TRUE;
}

////////////////////////////////////////////////////////////////////////
// Returnes The Path to the directory the PROGRAM file is running in //
// This is NOT the same as getcwd() which returns the base directory //
////////////////////////////////////////////////////////////////////////
int GetCurrentDirectory(char *pBuff,int Len)
{
int bytes;
char *pch;

bytes = readlink("/proc/self/exe",pBuff,Len);
pch = (char *)memrchr(pBuff,'/',Len); // Strip off our Program Name
bytes = pch - pBuff+1;
pBuff[bytes] = '\0'; // And Terminate the resulting Dir String properly
return(bytes);
}


////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawMessageBox(cairo_t *cr, int cx, int tp,int Today,int Year)
{
char Str[80] = "";
double x,h;
//Today = 229;
cairo_text_extents_t extents;
int Xmascount = CalculateXmasCountdown(Today,Year); // Look for days to Xmas
int ClockChangeday = CalculateClockChangeDay(Today,Year); // Clock Change Day?
int BurnsNight = GetYearDay(25,1,Year);
int GOTcount = CalculateGoTCountdown(Today);
//Today = 25;
//printf("Count %d CCD %d Today %d\n",count,ClockChangeday,Today);
if (Today == ClockChangeday && Today > 150)
{
(tp == 1)? DrawXmasText(cr,cx,Xmascount): DrawClockChangeText(cr,cx,ClockChangeday);
}
else if (Today == ClockChangeday && Today < 150)
{
(tp == 1)? DrawGoTText(cr,cx,GOTcount): DrawClockChangeText(cr,cx,ClockChangeday);
}
else if (Today == BurnsNight && GOTcount != -1)
{
(tp == 1)? DrawGoTText(cr,cx,GOTcount): DrawBurnsNightText(cr,cx);
}
else if (Xmascount < 65)
{
DrawXmasText(cr,cx,Xmascount);
}
else if (Today == BurnsNight)
{
DrawBurnsNightText(cr,cx);
}
else if(GOTcount != -1)
{
DrawGoTText(cr,cx,GOTcount);
}

else if (Today == 22 && hours < 22)
{
// printf("Today %d hours %d\n",Today,hours);
DrawTestMessage(cr,cx);
}
cairo_set_source_rgb(cr,YELLOW);
cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, MessagePointSize);

cairo_text_extents(cr,Str, &extents);
x = (double)cx - (extents.width)/2.0;
h = extents.height * 1.1;
cairo_move_to(cr, x,h);
cairo_show_text(cr,Str);
}

//////////////// CalculateClockChangeDay(int Today, int year) //////////////////
// Calculates The Number of Days Left Till Christmas Day.
////////////////////////////////////////////////////////////////////////
int CalculateClockChangeDay(int Today,int Year)
{
char s[10];
struct tm info;
memset(&info,0,sizeof(struct tm));
//Today = 201;
// First Get The Day Of The week the end of the month our change Sits On
int day = GetYearDay(31,(Today < 150)?3:10,Year);
sprintf(s,"%hu %hu",day+1,Year);
strptime(s,"%j %Y",&info);
if (info.tm_wday == 0) // Sunday is the last day of the month?
info.tm_wday = 7; // Correct for it (Sat will be the week before)
// Go Back to the previous Saturday
day -= info.tm_wday;
return(day);
}

///////////// CalculateXmasCountdown(int Today, int year) //////////////
// Calculates The Number of Days Left Till Christmas Day.
////////////////////////////////////////////////////////////////////////
int CalculateXmasCountdown(int Today,int Year)
{
//Year = 2018;
//Today = GetYearDay(27,10,Year);
int EOYDay = GetYearDay(31,12,Year);
int DaysToXmas;
int XmasDay = GetYearDay(25,12,Year);

if (Today > XmasDay) // If we've just had xmas day
{
++Year;
XmasDay = GetYearDay(25,12,Year);
DaysToXmas = (EOYDay - Today) + XmasDay;
}
else
{
DaysToXmas = XmasDay - Today;
}
return(DaysToXmas);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawTestMessage(cairo_t *cr, int cx)
{
char Str[40];
cairo_text_extents_t extents;
cairo_font_extents_t fextents;

cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, MessagePointSize);
cairo_font_extents(cr, &fextents);

cairo_set_source_rgb(cr,YELLOW);
strcpy(Str,"Igg g gggg gggggge!!");
cairo_text_extents(cr,Str, &extents);
cairo_move_to(cr, (cx - (extents.width)/2),fextents.ascent);
cairo_show_text(cr,Str);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawBurnsNightText(cairo_t *cr, int cx)
{
char Str[40];
cairo_text_extents_t extents;
cairo_font_extents_t fextents;

cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, MessagePointSize);
cairo_font_extents(cr, &fextents);

cairo_set_source_rgb(cr,YELLOW);
strcpy(Str,"Its Burns night tonight!!");
cairo_text_extents(cr,Str, &extents);
cairo_move_to(cr, (cx - (extents.width)/2),fextents.ascent);
cairo_show_text(cr,Str);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawClockChangeText(cairo_t *cr, int cx, int day)
{
char Str[40];
cairo_text_extents_t extents;
cairo_font_extents_t fextents;

cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, MessagePointSize);
cairo_font_extents(cr, &fextents);

cairo_set_source_rgb(cr,YELLOW);
sprintf(Str,"The clocks go %s tonight!!",(day < 150)? "forward": "back");
cairo_text_extents(cr,Str, &extents);
cairo_move_to(cr, (cx - (extents.width)/2),fextents.ascent);
cairo_show_text(cr,Str);
}

////////////////////////////////////////////////////////////////////////
// Prints a countdown of the number of days left till Christmas //
// Starts At 100 days Ends On Christmas Day With A merry Xmas Message //
////////////////////////////////////////////////////////////////////////
void DrawXmasText(cairo_t *cr, int cx, int count)
{
char Str[40] = "";
cairo_text_extents_t extents;
cairo_font_extents_t fextents;

//count = 0;
cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, MessagePointSize);
cairo_font_extents(cr, &fextents);

switch(count)
{
case 0: // It's Christmas Day
{
cairo_set_source_rgb(cr,YELLOW);
strcpy(Str,"MERRY CHRISTMAS!!");
cairo_text_extents(cr,Str, &extents);
cairo_move_to(cr, (cx - (extents.width)/2),fextents.ascent);
cairo_show_text(cr,Str);
break;
}
default: // Countdown To Xmas day
{
cairo_set_source_rgb(cr,YELLOW);
sprintf(Str,"%s%d More %s till Christmas",
(count <= 5)? "Only ": "",count,
(count > 1)? "sleeps": "sleep");
cairo_text_extents(cr,Str, &extents);
cairo_move_to(cr, (cx - (extents.width)/2),fextents.ascent);
cairo_show_text(cr,Str);
}
}
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawDayText(cairo_t *cr)
{
char DayStr[80] = "";
double x,y;

if (mday == 0 && year == 0) // Not Set Up Condition
return;
// Format Day String..
// First Look for Special Occasions
// See if we are dealing with Easter
getscreenres(&DispInfo,FALSE); // Set False
cairo_text_extents_t extents;
cairo_font_extents_t fextents;
int Easterday = CalculateEasterSunday(year);

if (doy == Easterday)
strcpy(DayStr,"Easter Sunday");
else if (doy == Easterday-2)
strcpy(DayStr,"Good Friday");
else if (doy == Easterday+1)
strcpy(DayStr,"Easter Monday");
else if (doy == GetYearDay(25,12,year))
{
strcpy(DayStr,"Christmas Day");
}
else if(doy == GetYearDay(26,12,year))
{
strcpy(DayStr,"Boxing Day");
}
else if(doy == GetYearDay(24,12,year))
{
strcpy(DayStr,"Christmas Eve");
}
// Try New Year
else if(doy == GetYearDay(31,12,year))
{
strcpy(DayStr,"New Year's Eve");
}
else if(doy == GetYearDay(01,01,year))
{
strcpy(DayStr,"New Year's Day");
}
// St Georges Day?
else if(doy == GetYearDay(23,04,year))
{
strcpy(DayStr,"St George's Day");
}

else // Nope Nothing Fits - Do Default Day Text
{
sprintf(DayStr,"%s",Days[dow]);
}
cairo_set_source_rgb(cr,YELLOW);
cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); //CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, TimeDatePointSize);
cairo_font_extents (cr,&fextents);
cairo_text_extents(cr,DayStr, &extents);

x = ((DispInfo.WorkWidth / 4) * 3.0) - (extents.width / 2);
//h = DispAreaBottom + (fextents.height - DispInfo.deltaHeight);


//x = (cx*3) - (extents.width)/2;
//h = (double)fextents.height;
y = TextboxHeight - fextents.descent;
cairo_move_to(cr, x,y);
cairo_show_text(cr,DayStr);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawCopyrightText(cairo_t *cr, int cx)
{
cairo_text_extents_t extents;
cairo_font_extents_t fextents;

int Point = 16;
char T1[40];
char T2[80];
char T3[] = "Windows GUI version. Written in \'C\' using the gtk toolbox";

sprintf(T1,"Studio Clock V%s. ",version);
sprintf(T2,"(C) Bob Elliot HCR 2015 - %d",year);
cairo_set_source_rgb(cr,TOMATO);
cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);

cairo_set_font_size (cr, Point);
cairo_font_extents (cr,&fextents);
cairo_text_extents(cr,T1, &extents);

double h = DispInfo.DispHeight;
double txtoffset = fextents.height + fextents.descent;
cairo_move_to(cr,0,(h - (txtoffset*2)) - DispInfo.deltaHeight);

cairo_show_text(cr,T1);
cairo_move_to(cr,0,(h - (txtoffset)) - DispInfo.deltaHeight);
//cairo_move_to(cr, extents.width,(h - txtoffset) - DispInfo.deltaHeight);
cairo_show_text(cr,T2);
cairo_move_to(cr,0,h - (fextents.descent)- DispInfo.deltaHeight);
cairo_show_text(cr,T3);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawDateText(cairo_t *cr)
{
char SuffixStr[4];
char DayStr[6];
char MonthStr[20];
int day,mon,yr;
double x,y;

day = mday;
mon = month;
yr = year;

//day = 30;
//mon = 8;
//yr = 2019;
cairo_text_extents_t Suffixext;
cairo_text_extents_t Dayext;
cairo_text_extents_t Monthext;
cairo_font_extents_t fextents;
if (mday == 0 && yr == 0)
return;
getscreenres(&DispInfo,FALSE); // Set False
cairo_set_source_rgb(cr,YELLOW);
cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);

if (day == 1 || day == 21 || day == 31)
sprintf(SuffixStr,"%s"," st");
else if (day == 2 || day == 22)
sprintf(SuffixStr,"%s"," nd");
else if (day == 3 || day == 23)
sprintf(SuffixStr,"%s"," rd");
else
sprintf(SuffixStr,"%s"," th");

cairo_set_font_size (cr, TimeDatePointSize/2);
cairo_text_extents(cr,SuffixStr, &Suffixext);

cairo_set_font_size (cr, TimeDatePointSize);
cairo_font_extents (cr,&fextents);

sprintf(DayStr,"%d",day);
cairo_text_extents(cr,DayStr, &Dayext);
// Format Date String
sprintf(MonthStr," %s %d",Months[mon],yr);
cairo_text_extents(cr,MonthStr, &Monthext);

int Totalx = Suffixext.width + Dayext.width + Monthext.width;

//x = (cx*3) - (Totalx/2);

x = ((DispInfo.WorkWidth / 4.0) * 3.0) - (Totalx / 2.0);
y = TextboxHeight *2 - fextents.descent;

cairo_move_to(cr, x,y);
cairo_set_font_size (cr, TimeDatePointSize);
cairo_show_text(cr,DayStr);

cairo_set_font_size (cr, TimeDatePointSize/2);
cairo_move_to(cr, x+Dayext.width,y * 0.75);
cairo_show_text(cr,SuffixStr);

cairo_set_font_size (cr, TimeDatePointSize);
cairo_move_to(cr, x+Dayext.width+Suffixext.width,y);
cairo_show_text(cr,MonthStr);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawTimeText(cairo_t *cr, int cx)
{
char TimeStr[25];
int min;
int hour;
//int x; //,y;
cairo_text_extents_t extents;
cairo_font_extents_t fextents;

getscreenres(&DispInfo,FALSE); // Set False

if (mday == 0 && year == 0) // Not Set Up Condition
return;

// Correct for min > 30. i.e. 12:35 == 25 to 12
if (minutes > 30)
{
min = 60 - minutes;
hour = hours+1;
}
else
{
min = minutes;
hour = hours;
}
//Convert to 12 Hour Clock
hour = hour % 12;
// Correct For Noon And Midnight
if (hour == 0)
hour = 12;

if (hour == 12 && min == 0)
sprintf(TimeStr,"%s",(hours == 0)? "Midnight":"Midday");
//else if (Now->tm_hour != 12 && Now->tm_min == 0)
else if (hour != 12 && min == 0)
sprintf(TimeStr,"%d O'Clock",hour);
else if (minutes == 15)
sprintf(TimeStr,"Quarter past %d",hour);
else if (minutes == 30)
sprintf(TimeStr,"Half past %d",hour);
else if (minutes == 45)
sprintf(TimeStr,"Quarter to %d",hour);
else
{
sprintf(TimeStr,"%d %s %s %d"\
,min
,(min==1)?"minute":"minutes"
,(minutes > 30)?"to":"past"
,hour);
}
cairo_set_source_rgb(cr,YELLOW);
cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size (cr, TimeDatePointSize);
cairo_font_extents (cr,&fextents);
cairo_text_extents(cr,TimeStr, &extents);

double x = ((DispInfo.WorkWidth / 4) * 3.0) - (extents.width / 2);
double y = DispAreaBottom + (fextents.ascent); //height);

cairo_move_to(cr, x,y);
cairo_show_text(cr,TimeStr);

}

////////////////////////////////////////////////////////////////////////
// Retuns The Year Day Easter Sunday Falls On in The tested Year //
////////////////////////////////////////////////////////////////////////
int CalculateEasterSunday(int year)
{
int Y,a,c,e,h,k,L,month,day;
double b,d,f,g,i,m;

Y = year;
a = Y%19;
b = floor(Y/100);
c = Y%100;
d = floor(b/4);
e = (int)b%4;
f = floor((b+8)/25);
g = floor((b-f+1)/3);
h = (19*a+(int)b-(int)d-(int)g+15)%30;
i = floor(c/4);
k = c%4;
L = (32+2*e+2*(int)i-h-k)%7;
m = floor((a+11*h+22*L)/451);
month = (int)floor((h+L-7*m+114)/31);
day = (int)((h+L-7*(int)m+114)%31)+1;
return(GetYearDay(day,month,year));
}

////////////////////////////////////////////////////////////////////////
// Retruns TRUE if the tested year is a leap year //
////////////////////////////////////////////////////////////////////////
int yisleap(int year)
{
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
int GetYearDay(int day, int mon, int yr)
{
static const int days[2][13] =
{
{0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
{0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
};
int leap = yisleap(yr);
return days[leap][mon] + day;
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
POS ComputePos(float pos,float Size,float XOrigin, float YOrigin)
{
POS p;
p.x = (sin(2.0 * G_PI * (pos / 60.0)) * Size) + XOrigin;
p.y = (-1.0 * cos(2.0 * G_PI * (pos / 60.0)) * Size) + YOrigin;
return (p);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawNumbers(cairo_t *cr,int cx, double y)
{
cairo_text_extents_t extents;
char buf[5];
POS p;

cairo_set_source_rgb(cr, MAGENTA);
cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); //CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, 100);

for (int i = 1; i < 13; i++)
{
sprintf(buf,"%d",i);
cairo_text_extents(cr,buf, &extents);
p = ComputePos(i*5.0,cx*0.8,cx-extents.width/2,y+extents.height/2);
cairo_move_to(cr, p.x,p.y);
cairo_show_text(cr,buf);
}
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
static void quit_application(gpointer app)
{
// g_print ("You clicked \"Quit\"\n");

g_application_quit (G_APPLICATION (app));
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////

static void clicked(GtkWindow *window, GdkEventButton *event, gpointer user_data)
{
printf("in Clicked\n");

// toggle window manager frames
gtk_window_set_decorated(window, !gtk_window_get_decorated(window));
getscreenres(&DispInfo,TRUE); // Set Refresh True
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
static void show_hide_frame(GtkWindow *window)
{

/* toggle window manager frames */
gtk_window_set_decorated(window, !gtk_window_get_decorated(window));
getscreenres(&DispInfo,TRUE); // Set Refresh True
}
////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
int show_popup(GtkWidget *widget, GdkEvent *event)
{

const int RIGHT_CLICK = 3;

if (event->type == GDK_BUTTON_PRESS)
{

GdkEventButton *bevent = (GdkEventButton *) event;
if (bevent->button == RIGHT_CLICK)
{
gtk_menu_popup_at_pointer(GTK_MENU(widget),(GdkEvent *)bevent);
}
return TRUE;
}
return FALSE;
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawTicks(cairo_t *cr, int cx, double y)
{
int i;
POS pos;

cairo_set_line_width(cr, 6);

for ( i= 0; i < 60; i++)
{
if (i != seconds)
{
pos = ComputePos(i,(cx-5),0.0,0.0);
if (i % 5 == 0)
{
cairo_set_source_rgb(cr, GREEN);
cairo_move_to(cr, pos.x+cx, pos.y+y);
cairo_arc(cr,(pos.x*0.97)+cx, (pos.y*0.97)+y,15,0,2*M_PI);
cairo_fill(cr);
}
else
{
cairo_set_source_rgb(cr, MAGENTA);
cairo_move_to(cr, pos.x+cx, pos.y+y);
cairo_line_to(cr,((pos.x*0.95)+cx),((pos.y*0.95)+y));
cairo_stroke(cr);
}
}
}
}

////////////////////////////////////////////////////////////////////////
// Draws A Circle In The Middle Of The Clock //
////////////////////////////////////////////////////////////////////////
void DrawMiddle (cairo_t *cr, int cx, double y)
{

cairo_set_source_rgb(cr, BLUE);
cairo_move_to(cr, cx, y);
cairo_arc(cr,cx,y,20,0,2*M_PI);
cairo_fill(cr);

cairo_set_source_rgb(cr, ClockfaceColor);
cairo_move_to(cr, cx, y);
cairo_arc(cr,cx,y,4,0,2*M_PI);
cairo_fill(cr);

}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawSecondHand (cairo_t *cr, int cx, double y)
{
POS Pos;
float i;

// Draw Overhang line and circle
Pos = ComputePos(seconds,(cx * -0.2),cx,y);
cairo_set_source_rgb(cr, HandsColor);
cairo_set_line_width(cr, 15);
cairo_move_to(cr, Pos.x,Pos.y);
cairo_line_to(cr, cx,y);
cairo_stroke(cr);
cairo_set_source_rgb(cr, HandsColor);
cairo_move_to(cr, Pos.x,Pos.y);
cairo_arc(cr,Pos.x,Pos.y,18,0,2*M_PI);
cairo_fill(cr);
cairo_set_source_rgb(cr, ClockfaceColor);
cairo_move_to(cr, Pos.x,Pos.y);
cairo_arc(cr,Pos.x,Pos.y,3,0,2*M_PI);
cairo_fill(cr);
// Draw Tapered second hand
Pos = ComputePos(seconds,(cx * 0.90),cx,y);
cairo_set_source_rgb(cr, HandsColor);
cairo_set_line_width(cr, 2);
for (i = - 7.0; i < 7.0; i+= 1.0)
{
cairo_move_to(cr, (cx + i),y);
cairo_line_to(cr, Pos.x,Pos.y);
cairo_move_to(cr, cx,(y + i));
cairo_line_to(cr, Pos.x,Pos.y);
cairo_stroke(cr);
}

//And finally the Circle on the end
Pos = ComputePos(seconds,(cx * 0.96),cx,y);
cairo_move_to(cr, Pos.x,Pos.y);
cairo_arc(cr,Pos.x,Pos.y,15,0,2*M_PI);
cairo_fill(cr);
//cairo_set_source_rgb(cr, ClockfaceColor);
//cairo_move_to(cr, Pos.x,Pos.y);
//cairo_arc(cr,Pos.x,Pos.y,3,0,2*M_PI);
//cairo_fill(cr);

}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawMinuteHand (cairo_t *cr, int cx, double y)
{
POS Pos;
float i;

cairo_set_source_rgb(cr, HandsColor);
Pos = ComputePos(minutes+(seconds/60.0),(cx * 0.8),cx,y);

if (AlternativeClockHands == TRUE)
{
cairo_set_line_width(cr, 2);
for (i = - 9.0; i < 9.0; i+= 1.0)
{
cairo_move_to(cr, (cx + i),y);
cairo_line_to(cr, Pos.x,Pos.y);
cairo_move_to(cr, cx,(y + i));
cairo_line_to(cr, Pos.x,Pos.y);
cairo_stroke(cr);
}
}
else
{
cairo_set_line_cap(cr,CAIRO_LINE_CAP_ROUND);
cairo_set_line_width(cr, 10);
cairo_move_to(cr, cx,y);
cairo_line_to(cr, Pos.x,Pos.y);
cairo_stroke(cr);
// Reset tip type to default
cairo_set_line_cap(cr,CAIRO_LINE_CAP_BUTT);
}
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawHourHand (cairo_t *cr, int cx, double y)
{
POS Pos;
float i;
double hr = (double)hours;
double min = (double)minutes;

cairo_set_source_rgb(cr, HandsColor);
Pos = ComputePos(((((int)hr % 12) * 5.0))+((min * 5.0)/60.0),(cx*0.6),cx,y);
if (AlternativeClockHands == TRUE)
{
cairo_set_line_width(cr, 2);
for (i = - 13.0; i < 13.0; i+= 1.0)
{
cairo_move_to(cr, (cx + i),y);
cairo_line_to(cr, Pos.x,Pos.y);
cairo_move_to(cr, cx,(y + i));
cairo_line_to(cr, Pos.x,Pos.y);
cairo_stroke(cr);
}
}
else
{
cairo_set_line_cap(cr,CAIRO_LINE_CAP_ROUND);
cairo_set_line_width(cr, 15);
cairo_move_to(cr, cx,y);
cairo_line_to(cr, Pos.x,Pos.y);
cairo_stroke(cr);
// Reset tip type to default
cairo_set_line_cap(cr,CAIRO_LINE_CAP_BUTT);
}
}

////////////////////////////////////////////////////////////////////////
// Draws The Selected image On The Screen //
////////////////////////////////////////////////////////////////////////
void DrawImage(cairo_t *cr, int cx, cairo_surface_t *image)
{
double w,h;

w = cairo_image_surface_get_width(image);
h = cairo_image_surface_get_height(image);
double y = (WorkHeight / 2) + (DispInfo.deltaHeight / 2);
double x = cx - (w/2);
double dy = y - (h/2);

cairo_set_source_surface(cr,image,x,dy);
cairo_paint(cr);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawPhone1Display(cairo_t *cr,int cx, int dir)
{
double w,ih,sh;
char buf[20] = "";
cairo_text_extents_t extents;
cairo_pattern_t *pat;

if (DispInfo.InhibitDispDraw == TRUE)
return;

cairo_select_font_face (cr, "monospace",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);

getscreenres(&DispInfo,FALSE); // Set False
double h = DispboxHeight;
double y = DispAreaTop + ((DispboxHeight * 2) + (guardHeight * 3));
double x = (DispInfo.midx *2) + DispInfo.GuardWidth;

switch (dir)
{
case OFF:
{
cairo_set_line_width(cr, 1);
cairo_set_source_rgb(cr, DARKGRAY);
cairo_rectangle(cr,x,y,DispInfo.w,h);
cairo_stroke_preserve(cr);
cairo_fill(cr);
break;
}
case ON:
{
pat = cairo_pattern_create_linear(x,0.0,x + DispInfo.w,100.0);
cairo_pattern_add_color_stop_rgb(pat, 0.0, 1.000, 0.993, 0.000);
cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.00, 1.00, 0.00);
// cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.474, 0.623, 0.047);
cairo_rectangle(cr,x,y,DispInfo.w,h);
cairo_set_source (cr,pat);
cairo_fill(cr);
cairo_pattern_destroy(pat);
w = (double)cairo_image_surface_get_width(glob.phimage);
ih = (double)cairo_image_surface_get_height(glob.phimage);
sh = (ih/h);
cairo_surface_set_device_scale(glob.phimage,sh,sh);
ih = (double)cairo_image_surface_get_height(glob.phimage);
cairo_set_source_surface(cr,glob.phimage,x,y);
cairo_paint(cr);
strcpy(buf,"414104");
cairo_set_font_size (cr, 180);

cairo_set_source_rgb(cr,WHITE);
cairo_text_extents(cr,buf, &extents);
cairo_move_to(cr,(x+ih)-2,
((y+h/2) + extents.height/2)-2);
cairo_show_text(cr,buf);

cairo_set_source_rgb(cr,BLACK);
cairo_text_extents(cr,buf, &extents);
cairo_move_to(cr, (x + ih)+5,
((y + h/2) + extents.height/2)+5);
cairo_show_text(cr,buf);

cairo_set_source_rgb(cr,RED);
cairo_text_extents(cr,buf, &extents);
cairo_move_to(cr, x + ih,
((y + h/2) + extents.height/2));
cairo_show_text(cr,buf);
break;
}
}

}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawPhone2Display(cairo_t *cr,int cx,int dir)
{
double w,ih,sh;
char buf[20] = "";
cairo_text_extents_t extents;
cairo_pattern_t *pat;

if (DispInfo.InhibitDispDraw == TRUE)
return;

cairo_select_font_face (cr, "monospace",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);

getscreenres(&DispInfo,FALSE); // Set False
double h = DispboxHeight;
double y = DispAreaTop + ((DispboxHeight * 3) + (guardHeight * 4));
double x = (DispInfo.midx *2) + DispInfo.GuardWidth;

switch (dir)
{
case OFF:
{
cairo_set_line_width(cr, 1);
cairo_set_source_rgb(cr, DARKGRAY);
cairo_rectangle(cr,x,y,DispInfo.w,h);
cairo_stroke_preserve(cr);
cairo_fill(cr);
break;
}
case ON:
{
pat = cairo_pattern_create_linear(x,0.0,x + DispInfo.w,100.0);
cairo_pattern_add_color_stop_rgb(pat, 0.0, 1.000, 0.993, 0.000);
cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.00, 1.00, 0.00);
// cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.474, 0.623, 0.047);//
cairo_rectangle(cr,x,y,DispInfo.w,h);
cairo_set_source (cr,pat);
cairo_fill(cr);
cairo_pattern_destroy(pat);
w = (double)cairo_image_surface_get_width(glob.phimage);
ih = (double)cairo_image_surface_get_height(glob.phimage);
sh = (ih/h);
cairo_surface_set_device_scale(glob.phimage,sh,sh);
ih = (double)cairo_image_surface_get_height(glob.phimage);
cairo_set_source_surface(cr,glob.phimage,x,y);
cairo_paint(cr);
strcpy(buf,"432508");
cairo_set_font_size (cr, 180);

cairo_set_source_rgb(cr,WHITE);
cairo_text_extents(cr,buf, &extents);
cairo_move_to(cr,(x+ih)-2,
((y+h/2) + extents.height/2)-2);
cairo_show_text(cr,buf);

cairo_set_source_rgb(cr,BLACK);
cairo_text_extents(cr,buf, &extents);
cairo_move_to(cr,(x + ih)+5,
((y+h/2)+extents.height/2)+5);
cairo_show_text(cr,buf);

cairo_set_source_rgb(cr,RED);
cairo_text_extents(cr,buf, &extents);
cairo_move_to(cr, x + ih,
((y + h/2) + extents.height/2));
cairo_show_text(cr,buf);
break;
}
}

}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawMicLiveDisplay(cairo_t *cr,int cx,int dir)
{
char buf[20] = "";
cairo_text_extents_t extents;
cairo_pattern_t *pat;

if (DispInfo.InhibitDispDraw == TRUE)
return;

getscreenres(&DispInfo,FALSE); // Set False
double h = DispboxHeight;
double y = DispAreaTop + (DispboxHeight + (guardHeight*2));
double x = (DispInfo.midx *2) + DispInfo.GuardWidth;

cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);

switch (dir)
{
case OFF:
{
cairo_set_line_width(cr, 1);
cairo_set_source_rgb(cr, DARKGRAY);
cairo_rectangle(cr,x,y,DispInfo.w,h);
cairo_stroke_preserve(cr);
cairo_fill(cr);
break;
}
case ON:
{
pat = cairo_pattern_create_linear(x,0.0,x + DispInfo.w,h);
cairo_pattern_add_color_stop_rgb(pat, 0.0, 0.541, 0.137, 0.529);
cairo_pattern_add_color_stop_rgb(pat, 0.5, 0.914, 0.251, 0.341);
cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.949, 0.443, 0.129);
cairo_rectangle(cr,x,y,DispInfo.w,h);
cairo_set_source (cr,pat);
cairo_fill(cr);
cairo_pattern_destroy(pat);
strcpy(buf,"MIC LIVE");
cairo_set_font_size (cr, 180);

cairo_set_source_rgb(cr,WHITE);
cairo_text_extents(cr,buf, &extents);
cairo_move_to(cr,(cx *3)-(extents.width/2)-2,
((y+h/2)+extents.height/2)-2);
cairo_show_text(cr,buf);

cairo_set_source_rgb(cr,BLACK);
cairo_text_extents(cr,buf, &extents);
cairo_move_to(cr, (cx *3) - (extents.width/2)+5,
((y + h/2) + extents.height/2)+5);
cairo_show_text(cr,buf);

cairo_set_source_rgb(cr,RED);
cairo_text_extents(cr,buf, &extents);
cairo_move_to(cr, (cx *3) - (extents.width/2),
((y + h/2) + extents.height/2));
cairo_show_text(cr,buf);
break;
}
}

}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void DrawOnAirDisplay(cairo_t *cr,int cx,int dir)
{
char buf[20] = "";
cairo_text_extents_t extents;
cairo_pattern_t *pat;

// ((DispInfo.WorkWidth / 4) * 3.0) - (extents.width / 2);
if (DispInfo.InhibitDispDraw == TRUE)
return;

getscreenres(&DispInfo,FALSE); // Set False
double h = DispboxHeight;
double y = (DispAreaTop + guardHeight);
double x = (DispInfo.midx *2) + DispInfo.GuardWidth;

pat = cairo_pattern_create_linear(x,0.0,x + DispInfo.w,h);
cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);

// First Set Background
switch (dir)
{
case OFF:
{
cairo_pattern_add_color_stop_rgb(pat, 0.0, 0.00, 1.00, 0.00);
cairo_pattern_add_color_stop_rgb(pat, 0.5, 0.00, 1.00, 0.90);
cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.00, 1.00, 0.00);
break;
}
case ON:
{
cairo_pattern_add_color_stop_rgb(pat, 0.0, 1.000, 0.000, 0.000);
cairo_pattern_add_color_stop_rgb(pat, 0.5, 1.000, 0.949, 0.000);
cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.118, 0.588, 0.000);
break;
}
}
// printf("y = %f h = %f\n",y,h);
cairo_rectangle(cr,x,y,DispInfo.w,h);
cairo_set_source (cr,pat);
cairo_fill(cr);
/***/
// Now Set Up Text
strcpy(buf,(dir == OFF)? "OFF AIR": "ON AIR");
cairo_set_font_size (cr, 180);

cairo_set_source_rgb(cr,WHITE);
cairo_text_extents(cr,buf, &extents);
cairo_move_to(cr,(cx *3)-(extents.width/2)-2,((y+h/2)+extents.height/2)-2);
cairo_show_text(cr,buf);

cairo_set_source_rgb(cr,BLACK);
cairo_text_extents(cr,buf, &extents);
cairo_move_to(cr,(cx *3)-(extents.width/2)+5,((y+h/2)+extents.height/2)+5);
cairo_show_text(cr,buf);

if (dir == OFF)
cairo_set_source_rgb(cr,RED);
else
cairo_set_source_rgb(cr,RED); //GREEN);

cairo_text_extents(cr,buf, &extents);
cairo_move_to(cr, (cx *3) - (extents.width/2),
((y + h/2) + extents.height/2));
cairo_show_text(cr,buf);
/****/
cairo_pattern_destroy(pat);

}

////////////////////////////////////////////////////////////////////////
// Draws the Clock Face On The Screen //
////////////////////////////////////////////////////////////////////////
void DrawClockFace (cairo_t *cr, int cx, double y)
{
cairo_set_source_rgb(cr, ClockfaceColor);
double x = DispInfo.DispWidth/4;

// printf("y = %f\n",y);
cairo_move_to(cr, x,y);
cairo_arc (cr, x, y, MIN(x, y),0,2*M_PI);
cairo_fill(cr);
}

////////////////////////////////////////////////////////////////////////
// Initial Display Draw Setup Routine //
////////////////////////////////////////////////////////////////////////
void DrawBoxes(cairo_t *cr, int cx)
{

DrawOnAirDisplay(cr,cx,DispInfo.OnAirState);
DrawMicLiveDisplay(cr,cx,DispInfo.MicState);
DrawPhone1Display(cr,cx,DispInfo.Ph1State);
DrawPhone2Display(cr,cx,DispInfo.Ph2State);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
static gboolean draw_cb(GtkWidget *wgt, cairo_t *cr, gpointer data)
{
cairo_text_extents_t ext;
cairo_font_extents_t fext;
int nMin;
GtkWidget *win = gtk_widget_get_toplevel(DispInfo.window);
int Boxes = 4; // No Of Display Boxes (On Air, Mic, Phone 1 & Phone 2)

cairo_select_font_face (cr, "Arial",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, TimeDatePointSize);
cairo_font_extents (cr,&fext);
cairo_text_extents(cr,"Aig", &ext);
// Initialise DispInfo Info..
DispInfo.GuardWidth = DispInfo.DispWidth * 0.003;
DispInfo.w = (DispInfo.DispWidth / 2) - (DispInfo.GuardWidth*2);
DispInfo.InhibitDispDraw = FALSE;
// First make Clock Face
/***/
DrawClockFace(cr,DispInfo.midx,(WorkHeight/2)+(DispInfo.deltaHeight/2));
DrawTicks (cr, DispInfo.midx,(WorkHeight/2)+(DispInfo.deltaHeight/2));
DrawImage(cr,DispInfo.midx,glob.image);
DrawNumbers (cr,DispInfo.midx,(WorkHeight/2)+(DispInfo.deltaHeight/2));
DrawSecondHand (cr, DispInfo.midx,(WorkHeight/2)+(DispInfo.deltaHeight/2));
DrawMinuteHand (cr, DispInfo.midx,(WorkHeight/2)+(DispInfo.deltaHeight/2));
DrawHourHand (cr, DispInfo.midx,(WorkHeight/2)+(DispInfo.deltaHeight/2));
DrawMiddle(cr, DispInfo.midx,(WorkHeight/2)+(DispInfo.deltaHeight/2));
DrawTimeText(cr,DispInfo.midx );
DrawDayText(cr);
DrawDateText(cr);
DrawCopyrightText(cr, DispInfo.midx);
DrawMessageBox(cr,DispInfo.midx,trip,doy,year);
//
if (DispInfo.OnAirState != DispInfo.LastOnAirState || DispInfo.ResChange == TRUE)
{
DispInfo.LastOnAirState = DispInfo.OnAirState;
DrawOnAirDisplay(cr,DispInfo.midx,DispInfo.OnAirState);
}
if (DispInfo.MicState != DispInfo.LastMicState || DispInfo.ResChange == TRUE)
{
DispInfo.LastMicState = DispInfo.MicState;
DrawMicLiveDisplay(cr,DispInfo.midx,DispInfo.MicState);
}
if (DispInfo.Ph1State != DispInfo.LastPh1State || DispInfo.ResChange == TRUE)
{
DispInfo.LastPh1State = DispInfo.Ph1State;
DrawPhone1Display(cr,DispInfo.midx,DispInfo.Ph1State);
}
if (DispInfo.Ph2State != DispInfo.LastPh2State || DispInfo.ResChange == TRUE)
{
DispInfo.LastPh2State = DispInfo.Ph2State;
DrawPhone2Display(cr,DispInfo.midx,DispInfo.Ph2State);
}

DispInfo.ResChange = FALSE;
/*****/
return FALSE;
}

////////////////////////////////////////////////////////////////////////
// Selects The Correct Logo.png For The Tested Dates //
// - or defaults to hcr.png //
////////////////////////////////////////////////////////////////////////
void SelectCorrectLogo(cairo_surface_t *image)
{
int DOY = loctime->tm_yday+1;
int yr = loctime->tm_year+1900;
int Easterday = CalculateEasterSunday(yr);
//DOY = GetYearDay(14,8,yr);

char Logo[255];
cairo_surface_destroy(glob.image);

//////////////////// New Year Logo ////////////////////////////////////
if (DOY == GetYearDay(1,1,yr))
{
sprintf(Logo,"%s%s",ProgDir,"hcrNewYear.png");
}
////////////////////////// Pancake Day /////////////////////////////////
else if(DOY == Easterday - 47)
{
sprintf(Logo,"%s%s",ProgDir,"hcrPancakes.png");
}
///////////////////// St Valentines Day? ///////////////////////////////
else if (DOY == (GetYearDay(14,2,yr)))
{
sprintf(Logo,"%s%s",ProgDir,"hcrValentine.png");
}
///////////////////////// Easter? //////////////////////////////////////
else if ((DOY > (Easterday-7)) && (DOY <= (Easterday+1)))
{
sprintf(Logo,"%s%s",ProgDir,"hcrEaster.png");
}
/////////////////////// St Georges Day /////////////////////////////////
else if (DOY == (GetYearDay(23,4,yr)))
{
sprintf(Logo,"%s%s",ProgDir,"hcrStGeorgesDay.png");
}
/////////////////////// May The Fourth /////////////////////////////////
else if (DOY == (GetYearDay(4,5,yr)))
{
sprintf(Logo,"%s%s",ProgDir,"hcrMayTheFourth.png");
}
/////////////////////// August 14th ////////////////////////////////////
else if (DOY == (GetYearDay(14,8,yr)))
{
// printf("Looking for Aug 14th\n");
sprintf(Logo,"%s%s",ProgDir,"hcrAugust14.png");
}
/////////////////////// Halloween //////////////////////////////////////
else if((DOY >= GetYearDay(24,10,yr)) && (DOY <= GetYearDay(31,10,yr)))
{
sprintf(Logo,"%s%s",ProgDir,"hcrHalloween.png");
}
////////////////////// Bonfire Night ///////////////////////////////////
else if (DOY >= (GetYearDay(1,11,yr)) && DOY <= (GetYearDay(5,11,yr)))
{
sprintf(Logo,"%s%s",ProgDir,"hcrFireworks.png");
}
///////////////////// Remembrance Day //////////////////////////////////
else if (DOY >= (GetYearDay(6,11,yr)) && DOY <= (GetYearDay(11,11,yr)))
{
sprintf(Logo,"%s%s",ProgDir,"hcrPoppy.png");
}
///////////////////////// Christmas ////////////////////////////////////
else if (DOY >= GetYearDay(1,12,yr))
{
sprintf(Logo,"%s%s",ProgDir,"hcrChristmas.png");
}
////////////////// Default Image ///////////////////////////////////////
else
{

sprintf(Logo,"%s%s",ProgDir,"hcr.png");
}
glob.image = cairo_image_surface_create_from_png(Logo);
}


////////////////////////////////////////////////////////////////////////
// Here We Look At At Handle any changes in the studio light display //
////////////////////////////////////////////////////////////////////////
static gint display_handler (GtkWidget *widget)
{
double h,x,y;

getscreenres(&DispInfo,TRUE);
h = DispboxHeight;
x = DispInfo.midx *2;

if (DispInfo.OnAirState != DispInfo.LastOnAirState || DispInfo.ResChange == TRUE)
{
// y = (DispAreaTop + guardHeight);
// gtk_widget_queue_draw_area(widget,x,y,x,h * 1.01);
// gpioWrite(OnAirOut,DispInfo.OnAirState);
}

if (DispInfo.MicState != DispInfo.LastMicState || DispInfo.ResChange == TRUE )
{
y = DispAreaTop + (DispboxHeight + (guardHeight*2));
gtk_widget_queue_draw_area(widget,x,y,x,h * 1.01);
gpioWrite(MicOut,DispInfo.MicState);

}

if (DispInfo.Ph1State != DispInfo.LastPh1State || DispInfo.ResChange == TRUE )
{
y = DispAreaTop + ((DispboxHeight * 2) + (guardHeight * 3));
gtk_widget_queue_draw_area(widget,x,y,x,h * 1.01);
gpioWrite(Phone1Out,DispInfo.Ph1State);
}

if (DispInfo.Ph2State != DispInfo.LastPh2State || DispInfo.ResChange == TRUE )
{
y = DispAreaTop + ((DispboxHeight * 3) + (guardHeight * 4));
gtk_widget_queue_draw_area(widget,x,y,x,h * 1.01);
gpioWrite(Phone2Out,DispInfo.Ph2State);
}

return(TRUE);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
static gint time_handler (GtkWidget *widget)
{
static time_t curtime;
double a,x,y,w,h;

getscreenres(&DispInfo,FALSE);

curtime = time(NULL);
loctime = localtime(&curtime);
seconds = loctime->tm_sec;
minutes = loctime->tm_min;
hours = loctime->tm_hour;
doy = loctime->tm_yday+1;
dow = loctime->tm_wday;
month = loctime->tm_mon;
mday = loctime->tm_mday;
year = loctime->tm_year+1900;

if (seconds != last_seconds)
{
last_seconds = seconds;
if (seconds % 2)
{
trip ^= 1;
}
x = 0;
y = 0;
w = DispInfo.DispWidth/2;
h = (WorkHeight);
// Draw The Clock Face
if (doy != YearDay)
{
YearDay = doy;
SelectCorrectLogo(glob.image);
x = DispInfo.DispWidth/2;
y = 0;
w = DispInfo.DispWidth/2;
h = TextboxHeight*2;
// Draw The Day And Date
gtk_widget_queue_draw_area(DispInfo.clockArea,x,y,w,h);

}
gtk_widget_queue_draw_area(DispInfo.clockArea,x,y,w,h);
if (last_minutes != minutes)
{
last_minutes = minutes;
x = DispInfo.DispWidth/2;
y = WorkHeight - TextboxHeight;
h = TextboxHeight;
// Draw The Time Of Day
gtk_widget_queue_draw_area(DispInfo.clockArea,x,y,w,h);
}
}

//gtk_widget_queue_draw(widget);
return TRUE;
}

void config_callback(GtkWindow *window,GdkEvent *event, gpointer data)
{
int type = event->type;
getscreenres(&DispInfo,FALSE); // Set False
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
static void activate (GtkApplication* app, gpointer user_data)
{
GtkWidget *frame;
GtkWidget *ebox;
GtkWidget *pmenu;
GtkWidget *frameMenuItem;
GtkWidget *hideMenuItem;
GtkWidget *maximizeMenuItem;
GtkWidget *quitMenuItem;

if (strlen(ProgName) == 0)
sprintf(ProgName,"HCR STUDIO CLOCK");
else
{

}
DispInfo.window = gtk_application_window_new (app);
gtk_window_set_decorated (GTK_WINDOW (DispInfo.window), TRUE);
gtk_window_set_title (GTK_WINDOW (DispInfo.window),ProgName);

//gtk_window_fullscreen(GTK_WINDOW (DispInfo.window));
gtk_window_maximize(GTK_WINDOW (DispInfo.window));
GtkWidget *win = gtk_widget_get_toplevel(DispInfo.window);

gtk_window_set_gravity(GTK_WINDOW (DispInfo.window),
GDK_GRAVITY_NORTH_WEST);
gtk_container_set_border_width (GTK_CONTAINER (DispInfo.window), 0);
gtk_widget_set_app_paintable(DispInfo.window, TRUE);

GdkScreen *screen = gtk_widget_get_screen(DispInfo.window);
GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
gtk_widget_set_visual(DispInfo.window, visual);

getscreenres(&DispInfo,TRUE); // Init Screen Size Params

ebox = gtk_event_box_new();
gtk_container_add(GTK_CONTAINER(DispInfo.window), ebox);

DispInfo.clockArea = gtk_drawing_area_new ();
// Needs to be changed at some point to just the clock rectangle
// And all other boxes added
gtk_widget_set_size_request (DispInfo.clockArea,
DispInfo.DispWidth, DispInfo.DispHeight);

gtk_container_add (GTK_CONTAINER (ebox), DispInfo.clockArea);

g_signal_connect (DispInfo.clockArea, "draw",
G_CALLBACK (draw_cb), NULL);
g_signal_connect (DispInfo.clockArea, "delete-event",
G_CALLBACK(quit_application), app);
// G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (G_OBJECT(DispInfo.window),"configure-event",
G_CALLBACK (config_callback),NULL);

gtk_widget_add_events(DispInfo.window, GDK_BUTTON_PRESS_MASK);
gtk_widget_add_events(DispInfo.window, GDK_CONFIGURE);

g_signal_connect(DispInfo.window, "button-press-event",
G_CALLBACK(clicked), NULL);
pmenu = gtk_menu_new();

frameMenuItem = gtk_menu_item_new_with_label("Show/Hide Frame");
gtk_widget_show(frameMenuItem);
gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), frameMenuItem);

quitMenuItem = gtk_menu_item_new_with_label("Quit");
gtk_widget_show(quitMenuItem);
gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), quitMenuItem);


g_signal_connect_swapped(G_OBJECT(frameMenuItem), "activate",
G_CALLBACK(show_hide_frame), GTK_WINDOW(DispInfo.window));

g_signal_connect_swapped(G_OBJECT(quitMenuItem), "activate",
G_CALLBACK(quit_application), app);

g_signal_connect_swapped(G_OBJECT(DispInfo.window), "destroy",
G_CALLBACK(quit_application), app);

g_signal_connect_swapped(G_OBJECT(ebox), "button-press-event",
G_CALLBACK(show_popup), pmenu);

g_timeout_add (50, (GSourceFunc) time_handler, DispInfo.clockArea);
//g_timeout_add (100, (GSourceFunc) display_handler,DispInfo.clockArea);
GPIOInit(DispInfo.clockArea);
gtk_widget_show_all (DispInfo.window);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void GPIOCallBack(int Pin,int Edge,uint32_t tick)
{
double h,x,y;

getscreenres(&DispInfo,TRUE);
h = DispboxHeight;
x = DispInfo.midx *2;

switch (Pin)
{
case OnAirIn:
DispInfo.OnAirState=(ForceOnAirMode==-1)? Edge:ForceOnAirMode;
if (DispInfo.OnAirState != DispInfo.LastOnAirState || DispInfo.ResChange == TRUE)
{
y = (DispAreaTop + guardHeight);
// gtk_widget_queue_draw_area(DispInfo.clockArea,x,y,x,h * 1.01);
gtk_widget_queue_draw(DispInfo.window);
gpioWrite(OnAirOut,DispInfo.OnAirState);
printf("Got On Air Callback %d\n",Edge);
}

// gtk_widget_queue_draw(DispInfo.window);
break;
case MicIn:
DispInfo.MicState=(ForceMicMode==-1)? Edge:ForceMicMode;
break;
case Phone1In:
DispInfo.Ph1State=(ForcePhone1Mode==-1)? Edge:ForcePhone1Mode;
break;
case Phone2In:
DispInfo.Ph2State=(ForcePhone2Mode==-1)? Edge:ForcePhone2Mode;
}
//printf("In CallBack Pin %d edge = %d\n",Pin,Edge);
}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
void GPIOInit(GtkWidget *window)
{
// Set Input Ports
gpioSetMode(OnAirIn, PI_INPUT);
gpioSetMode(MicIn, PI_INPUT);
gpioSetMode(Phone1In, PI_INPUT);
gpioSetMode(Phone2In, PI_INPUT);
// Set Output Ports
gpioSetMode(OnAirOut, PI_OUTPUT);
gpioSetMode(MicOut, PI_OUTPUT);
gpioSetMode(Phone1Out, PI_OUTPUT);
gpioSetMode(Phone2Out, PI_OUTPUT);
// Set Pullup Resistors On Inputs
gpioSetPullUpDown(OnAirIn, PI_PUD_UP);
gpioSetPullUpDown(MicIn, PI_PUD_UP);
gpioSetPullUpDown(Phone1In, PI_PUD_UP);
gpioSetPullUpDown(Phone2In, PI_PUD_UP);
// Set Glitch Filters
gpioGlitchFilter(OnAirIn, 50000);

gpioGlitchFilter(MicIn, 1000);
gpioGlitchFilter(Phone1In, 1000);
gpioGlitchFilter(Phone2In, 1000);

gpioSetISRFunc(OnAirIn,EITHER_EDGE,-1,GPIOCallBack);
//gpioSetAlertFunc(OnAirIn,GPIOCallBack);
//gpioSetISRFunc(MicIn,EITHER_EDGE,3000,GPIOCallBack);
//gpioSetAlertFunc(MicIn,GPIOCallBack);
//gpioSetISRFunc(Phone1In,EITHER_EDGE,5000,GPIOCallBack);
//gpioSetAlertFunc(Phone1In,GPIOCallBack);
//gpioSetISRFunc(Phone2In,EITHER_EDGE,7000,GPIOCallBack);
//gpioSetAlertFunc(Phone2In,GPIOCallBack);

DispInfo.LastOnAirState = -1;
DispInfo.LastMicState = -1;
DispInfo.LastPh1State = -1;
DispInfo.LastPh2State = -1;

}

////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
int main (int argc, char **argv)
{
GtkApplication *app;
char buf[255] = "";
char *dest;
int status;
int x;
DispInfo.InhibitDispDraw = TRUE;
GetCurrentDirectory(ProgDir,sizeof(ProgDir)-1);

for (int i = 0; i < argc;++i)
{
if (strcasecmp(argv,"-ah") == 0)
{
AlternativeClockHands = TRUE;
}
else if (strcasecmp(argv,"-showtimer") == 0)
{
ForceShowTiming = ON;
}
else if (strcasecmp(argv,"-onair=on") == 0)
{
ForceOnAirMode = ON;
}
else if (strcasecmp(argv,"-onair=off") == 0)
{
ForceOnAirMode = OFF;
}
else if (strcasecmp(argv,"-mic=on") == 0)
{
ForceMicMode = ON;
}
else if (strcasecmp(argv,"-mic=off") == 0)
{
ForceMicMode = OFF;
}
else if (strcasecmp(argv,"-phone1=on") == 0)
{
ForcePhone1Mode = ON;
}
else if (strcasecmp(argv,"-phone1=off") == 0)
{
ForcePhone1Mode = OFF;
}
else if (strcasecmp(argv,"-phone2=on") == 0)
{
ForcePhone2Mode = ON;
}
else if (strcasecmp(argv,"-phone2=off") == 0)
{
ForcePhone2Mode = OFF;
}
else if (strncasecmp(argv[i],"-name=",6) == 0)
{
x = i; // Clone arg number
dest = (char *)argv[x]+6; // Point Over The -name= bit
sprintf(ProgName,"%s",dest); // Poke rest into the buffer
for (++x; x < argc; x++) // Then look for suceeding argv's
{
if (strncmp(argv[x],"-",1) == 0) // That don't contain a "-"
{
break; // If they do crash - we've found the next argv
}
else
{
strcat(ProgName," ");
strcat(ProgName,argv[x]);
}
}
}

/**
else if ((strcasecmp(argv[i],"--help") == 0)||(strcasecmp(argv[i],"-help") == 0))
{
printf("********************* Studio Clock Version %s *********************\n","8.x.x");
printf("Flags Useage:\n -ah = Use alternative design of clock hands\n");
printf(" -OnAir=ON/OFF = Force On Air Indicator Permanantly On or OFF\n");
printf(" -Mic=ON/OFF = Force On Mic Indicator Permanantly On or OFF\n");
printf(" -Phone1=ON/OFF = Force Phone 1 Indicator Permanantly On or OFF\n");
printf(" -Phone2=ON/OFF = Force Phone 1 Indicator Permanantly On or OFF\n");
printf(" -showtimer = Show interrupt elapsed time (For Engineering use only)\n");
printf("********************* Studio Clock Version %s *********************\n","8.x.x");
return(-1);
}
****/
}

argc = 0;
if (gpioInitialise() < 0)
{
printf("Pigpio failed to Start\n");
}
//GPIOInit();
// Default To Get Us Started
sprintf(buf,"%shcr.png",ProgDir);
glob.image = cairo_image_surface_create_from_png(buf);
sprintf(buf,"%shcrPhoneRed.png",ProgDir);
glob.phimage = cairo_image_surface_create_from_png(buf);

app = gtk_application_new ("Bob.Elliot.Studio.Clock", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
////////////////////////////////////////////////////////////////////////
////////////////////// On Program Termination //////////////////////////
gpioTerminate();
cairo_surface_destroy(glob.image);
cairo_surface_destroy(glob.phimage);
g_object_unref (app);
return status;
}


Bob

bobe001
Posts: 10
Joined: Wed Dec 09, 2015 11:01 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 2:36 pm

peter

My makefile looks like this

CC ?= gcc

PKG_DEPS = gtk+-3.0
PKG_CONFIG ?= pkg-config

#CFLAGS ?= -O2
#CFLAGS += -Wall -Wno-format -std=c99

CFLAGS += $(shell $(PKG_CONFIG) --cflags $(PKG_DEPS))
LDLIBS += $(shell $(PKG_CONFIG) --libs $(PKG_DEPS)) -lm -lpigpio

all: WClock

clean:
rm WClock

Cheers

Bob

User avatar
PeterO
Posts: 5623
Joined: Sun Jul 22, 2012 4:14 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 5:41 pm

Ok, if I had known it was soo big I wouldn't have asked :o Thebest thing is to try to reproduce the problem with the shortest amount of code :-) THEN I'll take a look at it ;)

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

User avatar
PeterO
Posts: 5623
Joined: Sun Jul 22, 2012 4:14 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 5:44 pm

incognitum wrote:
Mon Jan 28, 2019 2:21 pm
May also want to try playing with idle functions.

Code: Select all

gboolean redrawThings(gpointer user_data)
{
   gtk_widget_queue_draw (Info.widget);
   return FALSE; /* Only run once */
}

void MicCallback (int Pin, int Edge, uint32_t tick)
{
   /* Schedule redrawThings to be executed in main event loop */
   g_idle_add (redrawThings, 0);
}
While idle functions are useful, putting a "gtk_widget_queue_draw()" call in there is a bad idea. If I remember correctly, among other problems (like a non responsive GUI) it tends to lead to 100% CPU use.

AH :oops: , now I see your handler returns FALSE so it won't be a problem !
However ISTR queued redraws only happen when the even loop is idle anyway, so somehow this seems redundant.

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

aLotOfSensor
Posts: 11
Joined: Sun Sep 16, 2018 5:14 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 6:26 pm

You'd need to create a GTK thread, then have that GTK registered thread manually check the GPIO state. You cannot in any way touch the main UI or any GTK functions from any thread outside the main thread or main thread registered callbacks that has not been registered with GTK internally.

https://www.geany.org/manual/gtk/gtk-faq/x482.html

You'd need to manually create a thread like that posted above, then loop it and have it check the gpio states and update the UI accordingly. You're not going to be able to use functions like gpioSetISRFunc() or gpioSetAlertFunc() to make direct changes like the above codes are doing.

incognitum
Posts: 452
Joined: Tue Oct 30, 2018 3:34 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 6:30 pm

PeterO wrote:
Mon Jan 28, 2019 5:44 pm
However ISTR queued redraws only happen when the even loop is idle anyway, so somehow this seems redundant.
Are you sure that is the case?

Keep in mind that the Pigpio library does not use glib itself, so does not integrate with its event loop.
So I would expect callbacks to be invoked from another thread pigpio created, regardless whether you are using the istr function or the other.

User avatar
PeterO
Posts: 5623
Joined: Sun Jul 22, 2012 4:14 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 6:53 pm

incognitum wrote:
Mon Jan 28, 2019 6:30 pm
PeterO wrote:
Mon Jan 28, 2019 5:44 pm
However ISTR queued redraws only happen when the even loop is idle anyway, so somehow this seems redundant.
Are you sure that is the case?
Hmmm, that may have been the case for GTK2. GTK3 seems more sophisticated...
https://developer.gnome.org/gtk3/stable ... model.html
Keep in mind that the Pigpio library does not use glib itself, so does not integrate with its event loop.
Which is the whole point of what we are doing.....
So I would expect callbacks to be invoked from another thread pigpio created, regardless whether you are using the istr function or the other.
PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

bobe001
Posts: 10
Joined: Wed Dec 09, 2015 11:01 pm

Re: Pigpio and GTK

Mon Jan 28, 2019 10:57 pm

Hi Peter

I thought I was getting somewhere for a time, It seemed that it needed XinitThreads().. This seemed to improve things - The pigpio callback would run the gtk_widget_queue_draw() function - howeverw after about 10 - 15 toggles the screen would still freeze.. It does look very much as though clashes between Pigpio and GTK derived threads are the issue here.. So it's back to my original way forward.. Trying to get GTK to call an interrupt whenever, say, OnAirState changes..looking at the documentation it would appear that user defined interrups & callbacks are possible, but I can't see how to make that happen.. I have a polled version of the program working perfectly, but I really don't like messy solutions like polling if there is a better interrupt driven way.. Do you know of a way to init a user interrupt which will fire when the edge changes?

Bob

bobe001
Posts: 10
Joined: Wed Dec 09, 2015 11:01 pm

Re: Pigpio and GTK

Tue Jan 29, 2019 12:40 pm

Hi

Trying a different approach now to get PIGPIO to talk to GTK and cairo.. As I was failing due (I think) threading issues I decided to try and keep all of the drawing within 1 thread

I have put together the following program which I would have thought would work but it fails when trying to execute the cairo_surface_create_for_rectangle(). Any clues as to what I'm doing wrong?

#include <stdio.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <gtk/gtk.h>
#include <pigpio.h>

#define _GNU_SOURCE

#define OFF 0
#define ON 1

#define In 24
#define Out 18

#define RED 1.000, 0.000, 0.000

static void GPIOInitialise(void)
{
gpioSetMode(In,PI_INPUT);
gpioSetMode(Out,PI_OUTPUT);
gpioSetPullUpDown(Out,PI_PUD_UP);
gpioGlitchFilter(In,1000);
}

static void GPIOCallback (int Pin,int Edge,uint32_t tick)
{
cairo_surface_t *surf;
cairo_t *cr;
cairo_surface_create_for_rectangle (surf,1.0,1.0,100.0,100.0);
//cr = cairo_create(surf);

//cairo_set_source_rgb(cr, RED);
//cairo_fill(cr);

cairo_surface_destroy(surf);
//cairo_destroy(cr);

}

static void activate (GtkApplication *app, gpointer user_data)
{
GtkWidget *window;
GtkWidget *button_box;

window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Window");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

GPIOInitialise();
gtk_widget_show_all (window);
gpioSetAlertFunc(In,GPIOCallback);
}

int main (int argc, char **argv)
{
GtkApplication *app;
int status;
XInitThreads();
if (gpioInitialise() < 0)
{
g_print("GPIO Failed To Initialise");
return(-1);
}

app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);

g_object_unref (app);
gpioTerminate();
return status;
}
Cheers

Bob

User avatar
PeterO
Posts: 5623
Joined: Sun Jul 22, 2012 4:14 pm

Re: Pigpio and GTK

Tue Jan 29, 2019 1:51 pm

Code: Select all

cairo_surface_t *surf;

cairo_surface_create_for_rectangle (surf,1.0,1.0,100.0,100.0);
Read the documentation for cairo_surface_create_for_rectangle !

https://developer.gnome.org/cairo/stabl ... -rectangle
cairo_surface_t * cairo_surface_create_for_rectangle (cairo_surface_t *target,
double x,
double y,
double width,
double height);

......

target : an existing surface for which the sub-surface will point to.
I've never used the gtk_application stuff but it looks like you've not created any widgets inside the application so it is impossible to actually draw anything in the window.

My advice, forget the gtk_application , just make a top level window, put a drawing area inside it, and try to draw something in that first.
Have a look here for some help : http://www.peteronion.org.uk/GtkExample ... rials.html Note first few examples don't actually use glade !

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

Return to “C/C++”