Adding C code to Utilize GLADE Output File

Having seen what a GLADE file can do, let's get into the C code which utilizes it. (Some of this is repeated from earlier documentation.)

We start off with some notes about how to compile, what to include, and some housekeeping. Note that all relays are in place and wired at this point, but occupancy detectors are NOT.

#include "ABE_IoPi.h"#include "ABE_I2CSwitch.h"#include <gtk/gtk.h>#include <cairo.h>#include <assert.h>#include <glib.h>#include <stdio.h>#include <stdlib.h>#include <wiringPi.h>#include <string.h>
#include <time.h>#include <unistd.h>#include <signal.h>#include <linux/i2c-dev.h>
/* NOTE: To compile programs with wiringPi, you need to add: * -lwiringPi * to your compile line(s). The working build line is* gcc -Wall -o "%e" "%f" ABE_IoPi.c -lwiringPi `pkg-config --cflags --libs gtk+-3.0 glib-2.0` -rdynamic* followed by (in a terminal)* ./modelRR* to run.**/
// MAX_SIDINGS includes only single-ended sidings. They are the last // entries at the top of most data arrays.// sidings can be single-ended or multiple sections, including extensions. #define MAX_SIDINGS 6// MAX_REGULAR_BLOCKS includes only regular blocks, not single-ended // sidings or crossovers #define MAX_REGULAR_BLOCKS 8// MAX_BLOCKS includes 8 regular blocks, 6 single-ended sidings // and extensions, and not crossovers; 14 numbered 0 thru 13. #define MAX_BLOCKS MAX_REGULAR_BLOCKS+MAX_SIDINGS// #define MAX_BLOCKS 14// MAX_CROSSOVERS is added into MAX_GAPS only. No separate entry. #define MAX_CROSSOVERS 3// MAX_GAPS includes regular blocks, sidings, crossovers, NOT extensions = 14+3-1=16 #define MAX_GAPS MAX_BLOCKS+MAX_CROSSOVERS-1// #define MAX_GAPS MAX_BLOCKS+3// typically, using blocks 0 thru 7 and siding blocks 8,9,10 (total 11)// plus 3 crossovers// MAX_DETECTORS is the number of detectors, 8/card x 2 cards #define MAX_DETECTORS 16
// global vbls GtkWidget *global_window_main; int Global_Counter=0; GtkWidget *data_gather_label; GTimer *timer; guint data_acq_timeout_id; guint display_update_timeout_id; GtkWidget *g_lbl_hello; GtkWidget *g_lbl_count; GtkWidget *progress_bar; GtkWidget *layout_widget; GtkWidget *display_update_lbl; GError *err = NULL; // It is mandatory to initialize to NULL GtkWidget *status_display_win; // initialized NULL in main GtkWidget *status_display_area; // initialized NULL in main GTimer *time_tracker; gboolean Run_data_loop; GtkWidget *about; GtkWidget *global_widget_ptr; cairo_t *global_cr_ptr; gpointer global_data; guint global_width, global_height; GdkRGBA global_color; GtkStyleContext *global_context; void set_block_color(cairo_t *cr, int block_nmbr); static int disp_update_counter;
enum color {BLACK, BLUE, GRAY, RED, YELLOW, GREEN};enum alarm_display_type {OFF, GAPS_ONLY, GAPS_1_BLOCK, GAPS_ADJ_BLOCKS};
typedef enum color color; enum alarm_display_type alarm_display;
// ........volatile data arrays: raw_data[], buffer[], display_data[].............// Due to small number of blocks, use arrays instead of linked lists.struct Block_Data_type{ int block_no; // block identifier number int block_status; // 0=normal, 1=alarm,2=caution gboolean permit_status; // sidings and crossovers OK to enter gboolean block_direction; // left-running, FALSE=fwd,TRUE=reverse gboolean block_direction_changed; // flag TRUE=update needed gboolean block_direction_relay_set; // TRUE = already set; // FALSE=needs output signal int block_pwr;// 0=off, 1=pack#1, 2=pack#2, // 3=siding connected to adjacent block gboolean block_pwr_changed; gboolean block_pwrpack_relay_set; // TRUE = already set; // FALSE=needs output signal gboolean block_pwr_on_off_connect_relay_set; // TRUE = already set;// FALSE=needs output signal color loco_color; // normally green, special=yellow} ;// display_data[] is stabilized data used for display/control purposesstruct Block_Data_type display_data[MAX_BLOCKS];// blocks 0-7 plus yards 8-9-10

The following DETECTOR info is still under construction, and will probably change soon

struct Detector_Data_Type // fast-reads/buffers individual detector data only. { char value; // read into here gint64 timestamp1; // 1st, microseconds gint64 timestamp2; // rolling next-to-latest gint64 timestamp3; // latest gint64 delta; // elapsed time, start signal to latest or end signal gint64 delta2; // elapsed time, next-to-latest or end to latest signal};struct Detector_Data_Type raw_detector_data[MAX_DETECTORS];
struct Block_Detector_Data_Type // Ties detector data to blocks{// The following two, entrance/exit, are for designated by normal // running direction. "block_direction" does not affect these.// Watch out if running counter to normal direction! gboolean block_occupied; // set by software until reliable detector is installed.// gboolean block_occupied_changed; int block_which_entrance_detected[3]; // which way did it come in? 0=not entered gint64 time_in; // when, counts up while detector is darkened int block_which_exit_detected[3]; // which way did it go out? gint64 time_out; // when, counts upward while next block is being entered};struct Block_Detector_Data_Type detector_buffer[MAX_BLOCKS];gboolean long_train_test=FALSE; // init for now
struct Block_Status_Ind_Display_type{ enum color Occupied; // green=empty, blue=occupied, yellow=exited enum color when_in; // blue when entered, fades to gray // when no longer entering enum color when_out; // blue gradually fading to lt blue, // to gray when next block no longer entering enum color Power; // green=off or same as entering, red=??, yellow=?? enum color Direction; // green=fwd, yellow=rev enum color Gap_Enter_pwr; // green=same, yellow=off, red=different enum color Gap_Enter_dir; // green=same, red=different enum color Gap_Enter_occupied; // green=off or same as entering, red=??, yellow=approaching enum color Gap_Exit_pwr; // green=same, yellow=off, red=different enum color Gap_Exit_dir;// green=same, red=different enum color Gap_Exit_occupied; // green=empty, red=occupied, yellow=??} ;struct Block_Status_Ind_Display_type Block_Status_Ind_Array[MAX_BLOCKS]; // All (input) detector signals write to their own 3-deep set of // arrays, initiated here. The rest, output signals, do not require // debouncing and can be written directly to the display_data array, // above. The input arrays are defined as a detector count of // decimal 16, 0 thru 15, i.e. octal 00 thru 017 determined by // MAX_DETECTORS.struct block_status_array_struct{ int next_block_nmbr;int this_block_nmbr;int prev_block_nmbr; int next_block_occupied;int this_block_occupied;int prev_block_occupied; int next_block_direction;int this_block_direction;int prev_block_direction; int next_block_pwr;int this_block_pwr;int prev_block_pwr;} ;struct block_status_array_struct block_status_array;
struct Label_Ptr_type{ char dummy_buffer[16]; // Dummy space holder. Something is clobbering the first 16 bytes!!! GtkLabel *status_label_ptr; GtkLabel *pwr_label_ptr; GtkLabel *block_direction_ptr;// char status_label_text[8];};struct Label_Ptr_type label_ptr_array[MAX_BLOCKS];
struct Gap_label_ptr_type{ GtkLabel *label_ptr; int from_block_nmbr; int to_block_nmbr;};struct Gap_label_ptr_type gap_ptr_array[MAX_GAPS]; // block entrances + crossoversstruct Gap_label_ptr_type permit_array[6]; // 3 crossover + 3 siding entrance entrance permits
struct Switch_Ptr_type // main powerpack off/on (disconnect) switches{ GtkSwitch *switch_pwr_1; GtkSwitch *switch_pwr_2; gboolean main_pwr_1; // flags: ON or OFF gboolean main_pwr_2;};struct Switch_Ptr_type switch_ptr_array;
// ............layout topology.......................struct Block_Hdwe_type // hdwe pin addresses (currently min 6, max 10){ int block_no; // block identifier number char block_name[30]; // descriptive name int nmbr_outputs; // max 3 = on/off + direction + pwrpack#; 1 for sidings// int nmbr_inputs; // max 7 = occupied detector + # of entrances + # exits int nmbr_entrances; // max 3 int nmbr_exits; // max 3 // all OUTPUT signals (may want to add on-layout signal LEDs later.) int OffOn_pinMode_nmbr; int FwdRev_pinMode__nmbr; int Pwr1_2_pinMode__nmbr; // Count entrances, exits from fwd direction main entrance // all INPUT signals (allowance for 3 entrances/exits, 0 thru 2) int occupied_pinMode_nmbr; // occupation detector: to be determined int enter_from_block_nmbr[3]; // entering from where? int enter_detect_pin_nmbr[3]; int exit_to_block_nmbr[3]; // exiting to where? int exit_detect_pin_nmbr[3];};struct Block_Hdwe_type block_hdwe[MAX_BLOCKS];
typedef struct block_structure // pin nmbrs are octal (char){ int block_no[14]; //except this one, which is int char pwr_select_pin[14]; char block_on_off_pin[14]; char block_fwd_rev_pin[14]; char siding_on_off_pin[14];} block_structure;
block_structure block_relay = // pin nmbrs are octal (char){ .block_no ={0,1,2,3,4,5,6,7,8,9,10,11,12,13}, //except this one, which is int .pwr_select_pin = {01,03,05,07,011,013,015,017,00,00,00,00,00,00}, .block_on_off_pin = {00,02,04,06,010,012,014,016,01,00,05,03,02,07}, .block_fwd_rev_pin = {07,06,05,04,03,02,01,00,010,010,010,010,010,010}, .siding_on_off_pin = {017,017,017,017,017,017,017,017,011,012,013,014,015,010}};
// Specify which i2c addresses to use.// For two IO PI Plus 2.1 cards, the following four addresses are hard-wired.// They can be char (octal) numbers, although specified as hexadecimal.// For two IO PI Plus 2.1 cards, the following four addresses are // hard-wired.char ADDRESS20=0x20; // right (aux) two relay cards 17 - 0char ADDRESS21=0x21; // left (main block) two relay cards 17 - 0char ADDRESS22=0x22; // fwd/rev relays 7 thru 0 (invert required)char ADDRESS23=0x23; // detectors
char on=0;char off=1; // global pin on/off zero or onechar on_invert=1;char off_invert=0; // global pin on/off one, zero
// SPECIFY BLOCK (RELAYS) TO TEST (pin nmbrs are octal)char main_pwr_pin=017; // main pwr pack #1 off/onchar pwr_select_pin; // block pwr pack select (#1 or #2)char block_on_off_pin; // block select off/onchar block_fwd_rev_pin; // block select fwd/rev
/** modelRR MAIN FILE* Interface controls:* (1) (Main Control Board) Computer interface active, or manual? long_train_test* (relay energized or not)status_display_area* (2) (Block) track block direction relays (assumes right-running).* (3) (Block) power pack selection: main or local.* (4) (Siding only) electrical connection to adjacent block.* Interface reads:* (1) All turnout positions normal/reverse (via current sensing?).* (2) All occupancy detectors (De-bounced? IR, or Hall effect?).* (3) All power packs voltage and (smoothed) current.* (4) All power packs on/off.* Interface displays* (1) Layout tracks.* (2) Track occupancy (via color).* (3) Track direction and power pack selections.* (4) Power packs voltage/current/on-or-off.* Smart displays:* (1) Possible derailment (flashing yellow track block).* (2) Possible short circuit (flashing red track block).* (3) Possible crossover misalignment (only one turnout of two)* (4) General alarm conditions.* */// ......forward declarations...................... void on_file_opn_toggle_data_acq_activate(); // fwd dec int handle_all_status_labels(GtkLabel *stat_label, int shortcut); // fwd dec int handle_all_direction_labels(GtkLabel *dir_label); // fwd dec gboolean process_layout_data_for_display(); // fwd dec void on_block_01_pwr_button_press_event(); // fwd dec void on_block_02_pwr_button_press_event(); // fwd dec void on_block_03_pwr_button_press_event(); // fwd dec void on_block_04_pwr_button_press_event(); // fwd dec void on_block_05_pwr_button_press_event(); // fwd dec void on_block_06_pwr_button_press_event(); // fwd dec void on_block_07_pwr_button_press_event(); // fwd dec void on_block_00_pwr_button_press_event(); // fwd dec void on_block_08_pwr_button_press_event(); // fwd dec void on_block_09_pwr_button_press_event(); // fwd dec void on_block_10_pwr_button_press_event(); // fwd dec void on_block_11_pwr_button_press_event(); // fwd dec void on_block_12_pwr_button_press_event(); // fwd dec void on_block_13_pwr_button_press_event(); // fwd dec
void turn_all_relays_off(){ for(char i=017;((i>=000)&&(i<=017));i--)write_pin(ADDRESS20,i,off); for(char i=017;((i>=000)&&(i<=017));i--)write_pin(ADDRESS21,i,off); for(char i=007;((i>=000)&&(i<=007));i--)write_pin(ADDRESS22,i,off_invert);// for(char i=017;((i>=000)&&(i<=017));i--)write_pin(ADDRESS23,i,off);}
void map_pins_to_blocks(){ // initialize pin-block mapping//#define ADDRESS20 0x20 // right (aux) two relay cards 17 – 0 // (main power packs off/on, sidings) IOPi_init(ADDRESS20); // initialize io pi channel on i2c set_port_direction(ADDRESS20, 0, 000); // set bus 0 to be outputs set_port_direction(ADDRESS20, 1, 000); // set bus 1 to be outputs//#define ADDRESS21 0x21 // left (main blocks) two relay cards 17 – 0// (block pack selection, off/on) IOPi_init(ADDRESS21); // initialize io pi channel on i2c set_port_direction(ADDRESS21, 0, 000); // set bus 0 to be outputs set_port_direction(ADDRESS21, 1, 000); // set bus 1 to be outputs//#define ADDRESS22 0x22 // mid left (main blocks) fwd/rev relays 7 thru 0// (fwd/rev direction, invert required) IOPi_init(ADDRESS22); // initialize io pi channel on i2c set_port_direction(ADDRESS22, 0, 000); // set bus 0 to be outputs set_port_direction(ADDRESS22, 1, 000); // set bus 1 to be outputs//#define ADDRESS23 0x23 // far left (main block gaps) detectors IOPi_init(ADDRESS23); // initialize io pi channel on i2c set_port_direction(ADDRESS23, 0, 001); // set bus 0 to be INputs set_port_direction(ADDRESS23, 1, 001); // set bus 1 to be INputs}

Now, let's skip to a few subroutines.

void turn_all_relays_off(){ for(char i=017;((i>=000)&&(i<=017));i--)write_pin(ADDRESS20,i,off); for(char i=017;((i>=000)&&(i<=017));i--)write_pin(ADDRESS21,i,off); for(char i=007;((i>=000)&&(i<=007));i--)write_pin(ADDRESS22,i,off_invert);// for(char i=017;((i>=000)&&(i<=017));i--)write_pin(ADDRESS23,i,off);}
void map_pins_to_blocks(){ // initialize pin-block mapping//#define ADDRESS20 0x20 // right (aux) two relay cards 17 – 0 // (main power packs off/on, sidings) IOPi_init(ADDRESS20); // initialize io pi channel on i2c set_port_direction(ADDRESS20, 0, 000); // set bus 0 to be outputs set_port_direction(ADDRESS20, 1, 000); // set bus 1 to be outputs//#define ADDRESS21 0x21 // left (main blocks) two relay cards 17 – 0// (block pack selection, off/on) IOPi_init(ADDRESS21); // initialize io pi channel on i2c set_port_direction(ADDRESS21, 0, 000); // set bus 0 to be outputs set_port_direction(ADDRESS21, 1, 000); // set bus 1 to be outputs//#define ADDRESS22 0x22 // mid left (main blocks) fwd/rev relays 7 thru 0// (fwd/rev direction, invert required) IOPi_init(ADDRESS22); // initialize io pi channel on i2c set_port_direction(ADDRESS22, 0, 000); // set bus 0 to be outputs set_port_direction(ADDRESS22, 1, 000); // set bus 1 to be outputs//#define ADDRESS23 0x23 // far left (main block gaps) detectors IOPi_init(ADDRESS23); // initialize io pi channel on i2c set_port_direction(ADDRESS23, 0, 001); // set bus 0 to be INputs set_port_direction(ADDRESS23, 1, 001); // set bus 1 to be INputs}

We'll skip some code for now, such as subroutines to initialize the arrays and define the layout hardware. We'll skip lightly over some bare-bones callback functions. These we'll later set up to input the detector data, but for now we'll implement code to update the display and re-draw it. Then, we'll see what is needed in "main" to incorporate the GLADE interface.

Here we go with the "fast" callback subroutine which will read the block occupancy detectors.

gboolean read_raw_data (gpointer data) // every 300 milliseconds{ int i;//,code; // ,xx,xxx,j;// Process raw_data from hdwe into buffer // and set the xxx_changed flags to TRUE. // Updates pulsing progress bar, // then reads & stores raw data into a buffer and sets xxx_changed datum, // or leaves it alone in raw_data array and leaves xxx_changed datum // so display_data array can be updated appropriately later (not here). // This de-bounces detector data. gtk_progress_bar_pulse (GTK_PROGRESS_BAR (data));
// iterate on blocks // Note that this does only detector (input) signals. All other // control (output) signals write directly to the display_data array. // All (input) detector signals write to their own 3-deep set of // arrays, initiated here. The rest, output signals, do not require // debouncing and can be written directly to the display_data array.
// The input arrays are defined as a detector count of decimal 16, // 0 thru 15, i.e. octal 00 thru 017 determined by MAX_DETECTORS.
// Loop on detectors first// Timing is for future detector logic for(i=0;i<MAX_DETECTORS;i++) // get current detector values { // block entering detector char value=read_pin(ADDRESS23,i); // get current value if(value==1) // DET ACTIVE, <<---MAY NEED TO INVERT THIS { if(raw_detector_data[i].value==0) { // DET ACTIVE, 1ST TIME TRIGGERED. initialize detector_buffer[i].block_occupied=TRUE; raw_detector_data[i].value=value; // timestamp in microseconds if track block is active raw_detector_data[i].timestamp1 =raw_detector_data[i].timestamp2 =raw_detector_data[i].timestamp3 =g_get_monotonic_time(); raw_detector_data[i].delta =raw_detector_data[i].delta2 =0;// // flag as occupied [DEVELOPMENT CODE]// detector_buffer[i].block_occupied=TRUE; // maybe???// detector_buffer[i].block_occupied_changed=TRUE; detector_buffer[i].time_in=detector_buffer[i].time_out=0; } // Det active, 1st time triggered, newly occupied else { // DET ACTIVE, occupied, 2nd & subsequent times // fill in for delta-time test raw_detector_data[i].timestamp2=raw_detector_data[i].timestamp3; raw_detector_data[i].timestamp3=g_get_monotonic_time(); // timestamp1 still equals 1st trigger time // timestamp2 equals next-to-last trigger time // timestamp3 equals this latest trigger time raw_detector_data[i].delta=raw_detector_data[i].timestamp2 -raw_detector_data[i].timestamp1; raw_detector_data[i].delta2=raw_detector_data[i].timestamp3 -raw_detector_data[i].timestamp2; } // Det active, occupied, 2nd & subsequent times } // if(raw_detector_data[i].value=0) else // DET NOT ACTIVE { if(detector_buffer[i].block_occupied) // occupied/not-active { raw_detector_data[i].timestamp3 =g_get_monotonic_time(); raw_detector_data[i].delta2=raw_detector_data[i].timestamp3 -raw_detector_data[i].timestamp2; // time since end-of-signal // timestamp1 still equals 1st trigger time // timestamp2 still equals final trigger time // timestamp3 equals current time, and is after // the last trigger time but block is still // listed as occupied. Once block is registered as // empty, it'll quit hitting here. } // skip empty block } // if(raw_detector_data[i].value=0) ELSE // cases: delta=0 && delta2=0 --> xxx, 1st pass, go again // delta=0 && delta2=xxx --> xxx, 2nd pass, go again // delta=xxx && delta2-delta<1000 or so, // 3rd or more pass, obscured but timestamp1 good) // delta=xxx && delta2-delta>1000, // no longer obscured (move on, timestamp1 is good) } // for(i=0;i<MAX_DETECTORS;i++)
// INFO for mapping detector data to gaps and blocks: // gap_ptr_array[MAX_GAPS=16] vs. block_hdwe[MAX_BLOCKS=14].block_no // where MAX_BLOCKS = MAX_REGULAR_BLOCKS+MAX_SIDINGS = 14 // line___ gap______ gap_ptr_array[] block_hdwe[].block_no___ // OUT1 gap_00_02 00 block boundary 00 // OUT2 gap_01_00 01 " 01 // OUT3 gap_02_04 02 " 02 // OUT4 gap_03_01 03 " 03 // OUT5 gap_04_06 04 " 04 // OUT6 gap_05_03 05 " 05 // OUT7 gap_06_07 06 " 06 // OUT8 gap_07_05 07 block boundary 07 // OUT1 spare 08 -- 08 // OUT2 gap_02_01 09 crossover 09 // OUT3 gap_05_06 10 " 10 // OUT4 gap_06_05 11 crossover 11 // OUT5 spare 12 sidings 8/11 12 ??? // OUT6 spare 13 sidings 9/12 13 ??? // OUT7 spare 14 siding 10/13 14 ??? // OUT8 spare 15 -- 15 ??? // make sense/use of raw_detector_data[] for regular blocks for(i=0;i<MAX_REGULAR_BLOCKS;i++) // blocks 0 thru 7 only {// read signals into raw_data, copy into buffer, and evaluate "changed" flags.// This section in progress; Condition truth table://block:in____ this__ out___ set_status_to_______ //1 none none none not occupied //2 none none signal occupied or leaving //3 none signal none short train occupied //4 signal none none approaching = occupied //5 signal signal none occupied entering //6 none signal signal long train in both //7 signal none signal two trains? PROBLEM? //8 signal signal signal PROBLEM// NEED CODE HERE! } // for(i=0;i<MAX_REGULAR_BLOCKS;i++)
// INSERT HERE logic for parsing status of the three crossovers// Request clearance manually, set direction, pwr, etc. If everything // checks out, show yellow OCCUPIED signals // on both of the two tracks. Set pwr/direction and clear signals // manually to restore normal running conditions. // for now, skip the spare at position 08!!!/* for(i=9;i<=11;i++) { // set occupation indicator color } // for(i=9;i<=11;i++)*/// INSERT HERE logic for parsing status of the sidings// Manually request siding, line up power, etc.// While train occupies siding, show yellow OCCUPIED signal// on both main and siding track. Set & clear manually. // for now, skip the spare at position 15!!!/* for(i=12;i<=14;i++) { } // for(i=12;i<=14;i++)*/ return TRUE;// Return true so function is called again; returning false removes the timeout function.} // gboolean read_raw_data (gpointer data)

Next, we'll process the display update code. This runs less frequently than the detector code, updating the display buffer every 5 seconds.

gboolean process_layout_data_for_display() {// Caled from slow (5-second) timer // via display_layout_and_status_data() timer function.// Processes input data raw_data from detector buffer into display_data array // and resets the xxx_changed flags to FALSE. Displays all // input & output signals, then check for urgent alarm and // caution conditionsint i,j; // int code; GtkLabel *dir_label; const gchar * state; char *format, *format_bkgnd; gchar *markup, *markup_bkgnd; int this_pwr_code, that_pwr_code, other_pwr_code, pwr_flag; int this_dir_code, that_dir_code, other_dir_code, dir_flag;
for(i=0;i<MAX_BLOCKS;i++) {
// look for status problems here, two items: // mismatched adjacent mainline blocks-in-series POWER PACKS = caution // mismatched adjacent mainline blocks-in-series DIRECTIONS = alarm if(block_hdwe[i].nmbr_exits!=0) // skip single-ended sidings { pwr_flag=dir_flag=0; this_pwr_code=display_data[i].block_pwr; this_dir_code=display_data[i].block_direction; that_pwr_code=that_dir_code=0; other_pwr_code=other_dir_code=0; for(j=0;j<=block_hdwe[i].nmbr_entrances-1;j++) // how to // skip pwr off?..siding connect? { if((display_data[block_hdwe[i].enter_from_block_nmbr[j]].block_no<8) &&(display_data[block_hdwe[i].enter_from_block_nmbr[j]].block_no>=0)) { that_pwr_code=display_data[block_hdwe[i].enter_from_block_nmbr[j]].block_pwr; if(this_pwr_code!=that_pwr_code) pwr_flag=2; // caution that_dir_code=display_data[block_hdwe[i].enter_from_block_nmbr[j]].block_direction; if(this_dir_code!=that_dir_code) dir_flag=1; // alarm } } // for(j=0... for(j=0;j<=block_hdwe[i].nmbr_exits-1;j++) { if(block_hdwe[i].exit_to_block_nmbr[j]<8) // rule out sidings { other_pwr_code =display_data[block_hdwe[i].exit_to_block_nmbr[j]].block_pwr; if(this_pwr_code!=other_pwr_code) pwr_flag=2;other_dir_code =display_data[block_hdwe[i].exit_to_block_nmbr[j]].block_direction; if(this_dir_code!=other_dir_code) dir_flag=1; } } // for(j-0... // set (or reset) "caution" i.e. 2 display_data[i].block_status =handle_all_status_labels(label_ptr_array[i].status_label_ptr,pwr_flag); // mismatched adjacent mainline blocks-in-series directions = alarm // alarm (but don't change) block direction dir_label=label_ptr_array[i].block_direction_ptr; state=gtk_label_get_text(dir_label); if(dir_flag>0) // set alarm { format="<span foreground=\"#000000\">%s</span>"; // black markup=g_markup_printf_escaped(format,state); format_bkgnd="<span background=\"#ff0000\">%s</span>"; // red markup_bkgnd=g_markup_printf_escaped(format_bkgnd,state); gtk_label_set_text( dir_label, state); gtk_label_set_markup(dir_label, markup); gtk_label_set_markup(dir_label, markup_bkgnd); } // if(dir_flag>0) else // formt_code=0 meaning rewrite with black-on-white (clear alarm) { format="<span foreground=\"#000000\">%s</span>"; // black markup=g_markup_printf_escaped(format,state); format_bkgnd="<span background=\"#eeeeee\">%s</span>"; markup_bkgnd=g_markup_printf_escaped(format_bkgnd,state); gtk_label_set_text( dir_label, state); gtk_label_set_markup(dir_label, markup); gtk_label_set_markup(dir_label, markup_bkgnd); } // if(dir_flag>0) else } // if(block_hdwe[i].nmbr_exits!=0) } // for(i=...)// set the gap displays, green if power packs are the same (or, for // sidings, connected), and directions are the same (except not checked// for sidings, opposite for 1-2, 5-6, 6-5) if((alarm_display!=OFF))// uses enum alarm_display {OFF, GAPS_ONLY, GAPS_1_BLOCK,// GAPS_ADJ_BLOCKS}; { int in_nmbr,out_nmbr; // process main block gap markers for(i=0;i<MAX_REGULAR_BLOCKS;i++) // first 8 are adjacent blocks { dir_label=gap_ptr_array[i].label_ptr; state=gtk_label_get_text(dir_label); in_nmbr=gap_ptr_array[i].from_block_nmbr; out_nmbr=gap_ptr_array[i].to_block_nmbr; this_pwr_code=display_data[i].block_pwr; this_dir_code=display_data[i].block_direction; that_pwr_code=display_data[in_nmbr].block_pwr; that_dir_code=display_data[in_nmbr].block_direction; if((this_pwr_code==that_pwr_code)&&(this_dir_code==that_dir_code)) { format="<span foreground=\"#eeeeee\">%s</span>"; markup=g_markup_printf_escaped(format,state); format_bkgnd="<span background=\"#00ff00\">%s</span>"; // green markup_bkgnd=g_markup_printf_escaped(format_bkgnd,state); gtk_label_set_text( dir_label, state); gtk_label_set_markup(dir_label, markup); gtk_label_set_markup(dir_label, markup_bkgnd); } else { format="<span foreground=\"#eeeeee\">%s</span>"; markup=g_markup_printf_escaped(format,state); format_bkgnd="<span background=\"#990000\">%s</span>"; // red markup_bkgnd=g_markup_printf_escaped(format_bkgnd,state); gtk_label_set_text( dir_label, state); gtk_label_set_markup(dir_label, markup); gtk_label_set_markup(dir_label, markup_bkgnd); } } // process siding gap markers for(i=MAX_REGULAR_BLOCKS;i<MAX_BLOCKS;i++) // 8,9,10,11,12,13 are sidings { dir_label=gap_ptr_array[i].label_ptr; state=gtk_label_get_text(gap_ptr_array[i].label_ptr); this_pwr_code=display_data[i].block_pwr; if(this_pwr_code!=0) { format="<span foreground=\"#eeeeee\">%s</span>"; markup=g_markup_printf_escaped(format,state); format_bkgnd="<span background=\"#00ff00\">%s</span>"; // green markup_bkgnd=g_markup_printf_escaped(format_bkgnd,state); gtk_label_set_text( dir_label, state); gtk_label_set_markup(dir_label, markup); gtk_label_set_markup(dir_label, markup_bkgnd); } else { format="<span foreground=\"#eeeeee\">%s</span>"; markup=g_markup_printf_escaped(format,state); format_bkgnd="<span background=\"#990000\">%s</span>"; // red markup_bkgnd=g_markup_printf_escaped(format_bkgnd,state); gtk_label_set_text( dir_label, state); gtk_label_set_markup(dir_label, markup); gtk_label_set_markup(dir_label, markup_bkgnd); } } // process crossover gaps for pwrpack,direction for(i=MAX_BLOCKS;i<MAX_GAPS;i++) //i=11;i<=13;i++) // 1-2,5-6,6-5 are crossovers { state=gtk_label_get_text(gap_ptr_array[i].label_ptr); in_nmbr=gap_ptr_array[i].from_block_nmbr; out_nmbr=gap_ptr_array[i].to_block_nmbr; this_pwr_code=display_data[in_nmbr].block_pwr; this_dir_code=display_data[in_nmbr].block_direction; that_pwr_code=display_data[out_nmbr].block_pwr; that_dir_code=display_data[out_nmbr].block_direction; if((this_pwr_code==that_pwr_code)&&(this_dir_code!=that_dir_code)) { format="<span foreground=\"#eeeeee\">%s</span>"; markup=g_markup_printf_escaped(format,state); format_bkgnd="<span background=\"#00ff00\">%s</span>"; //green markup_bkgnd=g_markup_printf_escaped(format_bkgnd,state); gtk_label_set_text( dir_label, state); gtk_label_set_markup(dir_label, markup); gtk_label_set_markup(dir_label, markup_bkgnd); } else { format="<span foreground=\"#eeeeee\">%s</span>"; markup=g_markup_printf_escaped(format,state); format_bkgnd="<span background=\"#990000\">%s</span>"; // red markup_bkgnd=g_markup_printf_escaped(format_bkgnd,state); gtk_label_set_text( dir_label, state); gtk_label_set_markup(dir_label, markup); gtk_label_set_markup(dir_label, markup_bkgnd); } } } // if(alarm_display!=OFF) return TRUE;} // gboolean process_layout_data_for_display()

In particular, have a look at how the color and text character are changed in the gaps, as reads, for example,

format="<span foreground=\"#eeeeee\">%s</span>"; markup=g_markup_printf_escaped(format,state); format_bkgnd="<span background=\"#990000\">%s</span>"; // red markup_bkgnd=g_markup_printf_escaped(format_bkgnd,state); gtk_label_set_text( dir_label, state); gtk_label_set_markup(dir_label, markup); gtk_label_set_markup(dir_label, markup_bkgnd);

This qualifies as ugly, in my opinion, but it works. First, you set the foreground text and text color up and store it. Then you set up the background color up and store it. Then you print the characters. Then you print the background. Whew! Figuring out how to do this cost quite a bit of time, especially with no debugger program available. The Visual C debugging environment spoiled me, I confess, as did interpreted Visual Basic and whatever we were using for DEC's LISP offering. The ability to set breakpoints, run to them, change the code, back up, and try again is sorely missed.

But enough tears shed. We next need to look at the screen update callback. This is the point where, whenever the machine deems it necessary to update the screen, this callback routine is called. I've abbreviated the code for brevity, but you can see how it's done.

gboolean layout_display_callback(GtkWidget *widget, cairo_t *cr,gpointer data){ // Update the layout display from the display_data[] array global_width = gtk_widget_get_allocated_width (widget); global_height = gtk_widget_get_allocated_height (widget);// Draws the layout diagram, based on techniques best illustrated in // http://zetcode.com/gfx/cairo/basicdrawing/ // see also https://cairographics.org/FAQ/ gboolean pwr,dir; // ..............draw the layout...................... // scale to unit square (0 to 1 width and height) cairo_save(cr); // save the original scalingcairo_scale(cr,global_width, global_height);cairo_set_line_width(cr,0.01);cairo_select_font_face(cr,"Purisa",CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr, 13);
// Sidings & crossovers 1st. See set_block_color() for siding display logic.// See also the various on_block_XX_pwr_button_press_event() callbacks.// Block 08 set_block_color(cr,8); cairo_arc (cr, 0.35, 0.38, 0.08, G_PI, 1.5 * G_PI); // LHS yard upper corner cairo_stroke(cr);// Block 09 set_block_color(cr,9); cairo_arc_negative (cr, 0.67, 0.43, 0.08, 0, 1.5* G_PI); // RHS yard 1st upper corner cairo_stroke(cr); // Block 10label_ptr_array set_block_color(cr,10); cairo_move_to(cr,.75,.1); cairo_line_to(cr,.80,.125); cairo_line_to(cr,.9,.125); cairo_stroke(cr); // cr, xcenter, ycenter, radius, angle1, angle2
// Draw the crossovers & sidings first, so trackage goes on top// lower LHS & RHS crossovers // check that blocks' power packs match and directions are different. pwr = (display_data[5].block_pwr==display_data[6].block_pwr); dir = (display_data[5].block_direction!=display_data[6].block_direction); if(pwr&&dir) cairo_set_source_rgb(cr,0,.8,0); // green else cairo_set_source_rgb(cr,1,0,0); // redlabel_ptr_array cairo_move_to(cr,.625,.35); // lower RHS crossover cairo_line_to(cr,.675,.3); cairo_stroke(cr); cairo_move_to(cr,.525,.35); // lower LHS crossover cairo_line_to(cr,.475,.3); cairo_stroke(cr);// Upper RHS crossover // check that blocks' power packs match and directions are different. pwr = (display_data[1].block_pwr==display_data[2].block_pwr); dir = (display_data[1].block_direction!=display_data[2].block_direction); if(pwr&&dir) cairo_set_source_rgb(cr,0,.8,0); // green else cairo_set_source_rgb(cr,1,0,0); // red cairo_move_to(cr,.72,.1); cairo_line_to(cr,.67,.2); cairo_stroke(cr);
// Now, main layout. upper top-level across horizontal straight tracks// Block 01 set_block_color(cr, 1); cairo_move_to(cr,.2,.1); cairo_line_to(cr,.9,.1); cairo_stroke(cr); // Block 02 set_block_color(cr, 2); cairo_move_to(cr,.2,.2); cairo_line_to(cr,.75,.2); cairo_stroke(cr); // Block 03 set_block_color(cr, 3);// cr, xc, yc, radius, angle1, angle2 cairo_arc (cr, 0.2, 0.2, 0.1, G_PI, 1.5 * G_PI); // upper LHS corner cairo_stroke(cr); // avoids trailing line while moving cairo_move_to(cr,.1,.2); // LHS vertical straight tracks cairo_line_to(cr,.1,.8); cairo_stroke(cr); cairo_arc (cr, 0.2, 0.8, 0.1, .5*G_PI, G_PI); // lower LHS corner cairo_stroke(cr); cairo_move_to(cr,.2,.9); // LHS lower horizontal straight tracks cairo_line_to(cr,.3,.9); cairo_stroke(cr); cairo_arc(cr, 0.3, 0.8, 0.1, 0, .5*G_PI); // inner LHS lower corner cairo_stroke(cr); cairo_move_to(cr,.4,.4); // inner LHS vertical straight tracks cairo_line_to(cr,.4,.8); cairo_stroke(cr); // Block 04 set_block_color(cr, 4); cairo_arc (cr, 0.2, 0.25, 0.05, G_PI, 1.5 * G_PI); // upper LHS corner cairo_stroke(cr); cairo_move_to(cr,.15,.25); // LHS vertical straight tracks cairo_line_to(cr,.15,.8); cairo_stroke(cr); cairo_arc (cr, 0.2, 0.8, 0.05, .5*G_PI, G_PI); // lower LHS corner cairo_stroke(cr); cairo_move_to(cr,.2,.85); // LHS lower horizontal straight tracks cairo_line_to(cr,.3,.85); cairo_stroke(cr); cairo_arc(cr, 0.3, 0.8, 0.05, 0, .5*G_PI); // inner LHS lower corner cairo_stroke(cr); cairo_move_to(cr,.35,.4); // inner LHS vertical straight tracks cairo_line_to(cr,.35,.8); cairo_stroke(cr); // Block 5 set_block_color(cr, 5); cairo_arc (cr, 0.45, 0.4, 0.05, G_PI, 1.5 * G_PI); // lower-level LHS upper corner cairo_stroke(cr); cairo_move_to(cr,.45,.35); // lower-level across horizontal straight tracks cairo_line_to(cr,.67,.35); cairo_stroke(cr); cairo_arc_negative (cr, 0.65, 0.4, 0.05, 0, 1.5*G_PI); // upper lower-level RHS corner cairo_stroke(cr);

...and so on. Next,

// // undo the scale cairo_restore(cr);// layout track drawing complete// display the counter cairo_set_source_rgb(cr,.8,.8,0); // yellowcairo_select_font_face(cr,"Purisa",CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr,13); cairo_move_to(cr,10,25); char str_count[50] = {0}; sprintf(str_count, "Diagram Refresh count %i", Global_Counter); cairo_show_text(cr,str_count); // display the legend. show what the alarm condition colors mean int legend_x=global_width/2.0-15; int legend_y=global_height/2.0; cairo_set_line_width(cr,5); cairo_set_source_rgb(cr,0,0,0); // black sprintf(str_count, "- -Legend- -"); cairo_move_to(cr,legend_x-5,legend_y); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,.5,.5,.5); // gray cairo_move_to(cr,legend_x,legend_y+12); cairo_line_to(cr,legend_x+8,legend_y+12); cairo_stroke(cr); sprintf(str_count, "Pwr #1"); cairo_move_to(cr,legend_x+15,legend_y+15); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,.2,.7,.7); // blue cairo_move_to(cr,legend_x,legend_y+27); cairo_line_to(cr,legend_x+8,legend_y+27); cairo_stroke(cr); sprintf(str_count, "Pwr #2"); cairo_move_to(cr,legend_x+15,legend_y+30); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,0,0,0); // black cairo_move_to(cr,legend_x,legend_y+42); cairo_line_to(cr,legend_x+8,legend_y+42); cairo_stroke(cr); sprintf(str_count, "pwr off"); cairo_move_to(cr,legend_x+15,legend_y+45); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,1,0,0); // red cairo_move_to(cr,legend_x,legend_y+68); cairo_line_to(cr,legend_x+8,legend_y+68); cairo_stroke(cr); sprintf(str_count, "Alarm"); cairo_move_to(cr,legend_x+15,legend_y+70); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,.8,.8,0); // yellow cairo_move_to(cr,legend_x,legend_y+82); cairo_line_to(cr,legend_x+8,legend_y+82); cairo_stroke(cr); sprintf(str_count, "Caution"); cairo_move_to(cr,legend_x+15,legend_y+85); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,0,.8,0); // green cairo_move_to(cr,legend_x,legend_y+98); cairo_line_to(cr,legend_x+8,legend_y+98); cairo_stroke(cr); sprintf(str_count, "Normal"); cairo_move_to(cr,legend_x+15,legend_y+100); cairo_show_text(cr,str_count);
// show fwd/reverse occupancy with cutesey little locomotives// This section eliminated for brevity
// at last, update the display gtk_widget_queue_draw(GTK_WIDGET(widget)); // redraws the drawing window!!! return FALSE;} // gboolean layout_display_callback()

Had enough yet? Not so fast! We need to do the status screen. This does some hard-coded checking, printing out color-coded status for each track block, starting with the occupied block forst (assuming there's only one, for now), showing for each block the status of the previous block, its status, and the next block's status. The screen capture is above, somewhere near the start of this topic.

First, some code to set the thing up.

struct rgb_struct // status color vector{double r;double g;double b;} ;// each block has three rgb, each of which has three (dir/pwr/occupied)
struct tri_color_struct{struct rgb_struct clr[9];};// three per block, one triplet each for occupied/dir/pwrstruct tri_color_struct rgb;
void Return_Block_Status(int block_nmbr){ // given block_nmbr, retrieve next/prev block nmbrs // for use in status window block_status_array.next_block_nmbr=block_hdwe[block_nmbr].exit_to_block_nmbr[0]; int next_block=block_status_array.next_block_nmbr; block_status_array.this_block_nmbr=block_nmbr; int this_block=block_status_array.this_block_nmbr; block_status_array.prev_block_nmbr=block_hdwe[block_nmbr].enter_from_block_nmbr[0]; int prev_block=block_status_array.prev_block_nmbr;
// retrieve direction/power/occupied status from display data block_status_array.next_block_occupied=detector_buffer[next_block].block_occupied; // FALSE= not block_status_array.next_block_direction=display_data[next_block].block_direction; // fwd=0 block_status_array.next_block_pwr=display_data[next_block].block_pwr; // 0=OFF, 1 or 2 = power pak
block_status_array.this_block_occupied=detector_buffer[this_block].block_occupied; block_status_array.this_block_direction=display_data[this_block].block_direction; block_status_array.this_block_pwr=display_data[this_block].block_pwr;
block_status_array.prev_block_occupied=detector_buffer[prev_block].block_occupied; block_status_array.prev_block_direction=display_data[prev_block].block_direction; block_status_array.prev_block_pwr=display_data[prev_block].block_pwr;
// rgb.clr[occ=0, dir=1, pwr=2] for next block// [3,4,5] for this block// [6,7,8] for prev block // ...NEXT block...(as it looks from THIS block) // occupied?next=%i\n",block_nmbr,block_status_array.prev_block_occupied,block_status_array.this_block_occupied,block_status_array.next_block_occupied); if ((block_status_array.next_block_occupied)) {rgb.clr[6].r=.6;rgb.clr[6].g=.6;rgb.clr[6].b=1;} // if next=occupied return blue else {rgb.clr[6].r=0;rgb.clr[6].g=1;rgb.clr[6].b=0;} // if this&next not-occupied default green // Direction?if(block_status_array.next_block_direction!=block_status_array.this_block_direction) {rgb.clr[7].r=1;rgb.clr[7].g=0;rgb.clr[7].b=0;} // red else // default green {rgb.clr[7].r=0;rgb.clr[7].g=1;rgb.clr[7].b=0;} // Power? if((block_status_array.next_block_pwr>0) // on &&(block_status_array.next_block_pwr==block_status_array.this_block_pwr)) {rgb.clr[8].r=0;rgb.clr[8].g=1;rgb.clr[8].b=0;} // proper stuff on = green else if ((block_status_array.next_block_pwr>0) // on &&(block_status_array.this_block_pwr>0) // also on but not matching &&(block_status_array.next_block_pwr!=block_status_array.this_block_pwr)) {rgb.clr[8].r=1;rgb.clr[8].g=0;rgb.clr[8].b=0;} // red else // default yellow {rgb.clr[8].r=1;rgb.clr[8].g=1;rgb.clr[8].b=0;} // ...THIS (current) block... //occupied? if ((block_status_array.this_block_occupied)) {rgb.clr[3].r=.6;rgb.clr[3].g=.6;rgb.clr[3].b=1;} // if this=occupied return lt blue else {rgb.clr[3].r=0;rgb.clr[3].g=1;rgb.clr[3].b=0;} // if next=not-occupied default green // Direction? // if next dir!=this dir, return red else default greenif(block_status_array.next_block_direction!=block_status_array.this_block_direction) {rgb.clr[4].r=1;rgb.clr[4].g=0;rgb.clr[4].b=0;} else // default green {rgb.clr[4].r=0;rgb.clr[4].g=1;rgb.clr[4].b=0;} // Power? // if this pwr=next, return pwr green, // else if next=off return pwr yellow, // else if next=on and not == this return pwr red if((block_status_array.this_block_pwr>0) // on &&(block_status_array.this_block_pwr>0)) {rgb.clr[5].r=0;rgb.clr[5].g=1;rgb.clr[5].b=0;} // proper stuff on = green else // default yellow {rgb.clr[5].r=.8;rgb.clr[5].g=.8;rgb.clr[5].b=0;} // ...PREViously occupied block...(as it looks from THIS block) // Occupied? // if prev=occupied and this = empty return occ green // else this=occupied and prev = occupied return occ red if ((block_status_array.prev_block_occupied)) {rgb.clr[0].r=.6;rgb.clr[0].g=.6;rgb.clr[0].b=1;} // if prev=occupied return blue else {rgb.clr[0].r=0;rgb.clr[0].g=1;rgb.clr[0].b=0;} // if next=not-occupied default green // Direction // if prev dir=this dir, return dir green else return yellow if(block_status_array.prev_block_direction!=block_status_array.this_block_direction) {rgb.clr[1].r=.8;rgb.clr[1].g=.8;rgb.clr[1].b=0;} else {rgb.clr[1].r=0;rgb.clr[1].g=1;rgb.clr[1].b=0;} // default green // Power // if prev=off, return pwr green, // else if prev=on and prev !=this return pwr red, // else if prev=on and prev ==this return pwr yellow if((block_status_array.this_block_pwr==block_status_array.prev_block_pwr) &&(((switch_ptr_array.main_pwr_1)&&(block_status_array.prev_block_pwr==1)) ||((switch_ptr_array.main_pwr_2)&&(block_status_array.prev_block_pwr==2)))) {rgb.clr[2].r=0;rgb.clr[2].g=1;rgb.clr[2].b=0;} // if prev=this=on, return pwr green, else if((block_status_array.prev_block_pwr>0) && (block_status_array.prev_block_pwr!=block_status_array.this_block_pwr)) {rgb.clr[2].r=1;rgb.clr[2].g=0;rgb.clr[2].b=0;} else {rgb.clr[2].r=0;rgb.clr[2].g=1;rgb.clr[2].b=0;} // default green} // void Return_Block_Status()
// Arranged so as to list blocks in run order, as specified// in that awful Initialize_data_arrays() topology routine (above)int block_list[MAX_REGULAR_BLOCKS]={0,2,4,6,7,5,3,1}; int block_display_list[MAX_REGULAR_BLOCKS]; // build on-the-fly
gboolean display_status_data(GtkWidget *widget, cairo_t *cr, gpointer data){ // draw the status display int x,y; // display the counter cairo_set_source_rgb(cr,.5,.5,.5); // gray cairo_select_font_face(cr,"Purisa",CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr,13); char str_count[80] = {0}; static char str_stat[55] = "O D P > O D P > O D P"; static char rev_str_stat[55] = "O D P < O D P < O D P"; sprintf(str_count, "Refresh count %i ", Global_Counter); cairo_move_to(cr,5,10); cairo_show_text(cr,str_count); sprintf(str_count, "O(ccupied)/D(irection)/P(ower)"); cairo_move_to(cr,24*8,10); cairo_show_text(cr,str_count); cairo_move_to(cr,10,25); sprintf(str_count, "Block - - - - Pwr - Fwd/Rev - Occupied - - - Prev Block -- This -- Next Block"); cairo_show_text(cr,str_count); // underline headers cairo_move_to(cr, 10,25); // block # cairo_line_to(cr, 9*8,25); cairo_stroke(cr); cairo_move_to(cr,12*8,25); // pwr pack cairo_line_to(cr,15*8,25); cairo_stroke(cr); cairo_move_to(cr,17*8,25); // fwd/rev cairo_line_to(cr,25*8,25); cairo_stroke(cr); cairo_move_to(cr,27*8,25); // occupied cairo_line_to(cr,36*8,25); cairo_stroke(cr); cairo_move_to(cr,40*8,25); // prev cairo_line_to(cr,50*8,25); cairo_stroke(cr); cairo_move_to(cr,51*8,25); // this cairo_line_to(cr,57*8,25); cairo_stroke(cr); cairo_move_to(cr,59*8,25); // next cairo_line_to(cr,68*8,25); cairo_stroke(cr); // do the entries: list each block's status // build list with occupied block at topint block_display_list_start=0;int ctr=0;// set up for display starting with occupied blockfor (int ii=MAX_REGULAR_BLOCKS;ii>0;ii--) // REGULAR BLOCKS ONLY{ // find an occupied block, if any. Default to block 00. if(detector_buffer[ii].block_occupied>0) {ctr=ii;}}// xlate to block_display_list subscriptfor (int i=0;i<MAX_REGULAR_BLOCKS;i++){if(block_list[i]==ctr) {block_display_list_start=i;} // offset into block_list[]}for (int ii=0;ii<MAX_REGULAR_BLOCKS;ii++) // REGULAR BLOCKS ONLY{ ctr=ii+block_display_list_start; if (ctr>=MAX_REGULAR_BLOCKS) {ctr=ctr-MAX_REGULAR_BLOCKS;} block_display_list[ii]=block_list[ctr];}// KLUGE to display blocks in run-orderfor (int ii=0;ii<MAX_REGULAR_BLOCKS;ii++) // REGULAR BLOCKS ONLY {// int i=block_list[ii]; // would have listed them in numerical order int i=block_display_list[ii]; // block name y=(ii+1)*20+20; x=10; cairo_set_source_rgb(cr,.2,.2,.2); // almost black cairo_move_to(cr,x,y); if(i<10) sprintf(str_count, "Block_0%i", i); else sprintf(str_count, "Block_%i", i); cairo_show_text(cr,str_count); // pwr pack cairo_set_source_rgb(cr,.2,.2,.2); // almost black cairo_move_to(cr,x+11*8,y); if((switch_ptr_array.main_pwr_1)&&(display_data[i].block_pwr==1)) {sprintf(str_count, "# 1 ");} else if((switch_ptr_array.main_pwr_2)&&(display_data[i].block_pwr==2)){sprintf(str_count, "# 2 ");} else {sprintf(str_count, "off ");} cairo_show_text(cr,str_count); // fwd/rev cairo_set_source_rgb(cr,.2,.2,.2); // almost black cairo_move_to(cr,x+16*8,y); if(display_data[i].block_direction) {sprintf(str_count, "rev ");} else {sprintf(str_count, "fwd ");} cairo_show_text(cr,str_count); // block status cairo_set_source_rgb(cr,.2,.2,.2); // almost black cairo_move_to(cr,x+26*8,y); if(detector_buffer[i].block_occupied) {sprintf(str_count, "occupied");} else {sprintf(str_count, "vacant");} // occupied or empty cairo_show_text(cr,str_count); // problem indicators: gap/direction/pwr incompatibilities cairo_select_font_face(cr,"Courier",CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_BOLD); cairo_set_line_width(cr,16.); // determine block status, set indicators Return_Block_Status(i); // output next/prev block nmbrs cairo_move_to(cr,x+39*8,y+2); // status sprintf(str_count, "0%i",block_status_array.prev_block_nmbr); cairo_show_text(cr,str_count); cairo_move_to(cr,x+45*8+150,y+2); sprintf(str_count, "0%i",block_status_array.next_block_nmbr); cairo_show_text(cr,str_count); // output status bars for (int k=2;k>=0;k--) // 0=next block, 1=this block, 2=previous block { // occupied (mismatch) cairo_set_source_rgb(cr,rgb.clr[k*3].r,rgb.clr[k*3].g,rgb.clr[k*3].b); cairo_move_to(cr,x+41*8+k*66,y-3); // status cairo_line_to(cr,x+43*8+k*66,y-3); cairo_stroke(cr); // direction (mismatch) cairo_set_source_rgb(cr,rgb.clr[k*3+1].r,rgb.clr[k*3+1].g,rgb.clr[k*3+1].b); cairo_move_to(cr,x+43*8+k*66,y-3); // status cairo_line_to(cr,x+45*8+k*66,y-3); cairo_stroke(cr); // power (mismatch) cairo_set_source_rgb(cr,rgb.clr[k*3+2].r,rgb.clr[k*3+2].g,rgb.clr[k*3+2].b); cairo_move_to(cr,x+45*8+k*66-1,y-3); // status cairo_line_to(cr,x+47*8+k*66,y-3); cairo_stroke(cr);// set occupancy aging display here } // for (int k=0;k<3;k++) cairo_set_source_rgb(cr,.2,.2,.2); // kinda white cairo_move_to(cr,x+41*8+4,y); // status if(display_data[i].block_direction) {cairo_show_text(cr,rev_str_stat);} // "O D P > O D P > O D P";} else {cairo_show_text(cr,str_stat);} // "O D P < O D P < O D P";} } // for (int i=0;i<MAX_BLOCKS;i++) // display the legend. show what the alarm condition colors mean cairo_select_font_face(cr,"Courier",CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_NORMAL); int legend_x=global_width/2.0-15; int legend_y=global_height/2.0+30; cairo_set_line_width(cr,5); cairo_set_source_rgb(cr,0,0,0); // black sprintf(str_count, "- -Legend- -"); cairo_move_to(cr,legend_x-5,legend_y); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,.5,.5,.5); // gray cairo_move_to(cr,legend_x,legend_y+12); cairo_line_to(cr,legend_x+8,legend_y+12); cairo_stroke(cr); sprintf(str_count, "Pwr #1"); cairo_move_to(cr,legend_x+15,legend_y+15); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,.2,.7,.7); // blue cairo_move_to(cr,legend_x,legend_y+27); cairo_line_to(cr,legend_x+8,legend_y+27); cairo_stroke(cr); sprintf(str_count, "Pwr #2"); cairo_move_to(cr,legend_x+15,legend_y+30); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,0,0,0); // black cairo_move_to(cr,legend_x,legend_y+42); cairo_line_to(cr,legend_x+8,legend_y+42); cairo_stroke(cr); sprintf(str_count, "pwr off"); cairo_move_to(cr,legend_x+15,legend_y+45); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,1,0,0); // red cairo_move_to(cr,legend_x,legend_y+68); cairo_line_to(cr,legend_x+8,legend_y+68); cairo_stroke(cr); sprintf(str_count, "Alarm"); cairo_move_to(cr,legend_x+15,legend_y+70); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,.8,.8,0); // yellow cairo_move_to(cr,legend_x,legend_y+82); cairo_line_to(cr,legend_x+8,legend_y+82); cairo_stroke(cr); sprintf(str_count, "Caution"); cairo_move_to(cr,legend_x+15,legend_y+85); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,0,.8,0); // green cairo_move_to(cr,legend_x,legend_y+98); cairo_line_to(cr,legend_x+8,legend_y+98); cairo_stroke(cr); sprintf(str_count, "Normal"); cairo_move_to(cr,legend_x+15,legend_y+100); cairo_show_text(cr,str_count);
cairo_set_source_rgb(cr,.6,.6,1); // lt blue cairo_move_to(cr,legend_x,legend_y+114); cairo_line_to(cr,legend_x+8,legend_y+114); cairo_stroke(cr); sprintf(str_count, "Occupied"); cairo_move_to(cr,legend_x+15,legend_y+116); cairo_show_text(cr,str_count); return FALSE; // returns 0 as good completion} // gboolean display_status_data()
gboolean status_display_callback(GtkWidget *widget, cairo_t *cr, gpointer data){ gboolean x; x=display_status_data(widget, cr, data); // at last, update the display cairo_set_source_rgb(cr,0,0,0); // black gtk_widget_queue_draw(GTK_WIDGET(widget)); // redraws the drawing window!!! return x;} // gboolean status_display_callback (GtkWidget *widget, cairo_t *cr, gpointer data)
gboolean display_layout_and_status_data (gpointer data){ // g_timeout_add() callback function. Comes here every 5 seconds. // Updates indicator, then displays current data via // process_layout_data_for_display(), which also checks for urgent // alarm and caution conditions. // Here, display_layout_and_status_data() checks for more routine // alarm and caution conditions switch(disp_update_counter) // update indicator {case 0: gtk_label_set_text(GTK_LABEL(display_update_lbl), "+"); disp_update_counter++; break; case 1: gtk_label_set_text(GTK_LABEL(display_update_lbl), "#"); disp_update_counter=0; break; default: gtk_label_set_text(GTK_LABEL(display_update_lbl), "X"); disp_update_counter=0; break; } process_layout_data_for_display(); // lets draw function display layout data Global_Counter++; // Check display_data, update/modify data items appropriately, // then clear display_data update flags
// Here, check for routine alarm and caution conditions
return TRUE;// Return true so the function will be called again // returning false removes the timeout function.} // gboolean display_layout_and_status_data (gpointer data)

Click here to see the details about Hooking it All Together with the main() function