"Colors" - A Palm Project Example

Jan Schaumann <jschauma@netmeister.org>

[netmeister.org] [netmeister's PalmStuff]

A brief walkthrough of the development of a simple program for the Palm OS

Note: This document was written in 2000 and has not been updated since then. I simply don't have the time to keep it up to date. If you wish to maintain the document, please let me know and I'll add a link to your website from here. Other questions and problems regarding this document I will most likely no longer be able to answer -- not out of malice, but actually sheer ignorance; I have not kept up to date on the Palm topic.


Table of contents

1. Introduction
2. Overview
3. "Colors" - A Palm Project Example
3.1 Project Definition/Specifications
3.2 Project Design/Layout
3.3 Resource Definitions
3.4 Main Program
3.5 Additional Routines and Procedures
3.6 Makefile
4. The Code
4.1 Callback.h
4.2 Colors.c
4.3 Colors.h
4.4 Colors.rcp
4.5 ColorsGeneral.c
4.6 ColorsRsc.h
4.7 EventHandlers.c
4.8 Hex2rgb.c
4.8.1 Plain C code for hex2rgb
4.9 Makefile
4.10 Rgb2hex
4.10.1 Plain C code for rgb2hex
5. Screenshot


 1. Introduction

This document shows and explains the different stages of the development of a program for the Palm OS Platform. It is assumed that you are working in a Linux/Unix environment and have successfully installed and familiarized yourself with the necessary tools (gcc, pilrc, POSE etc). If you haven't done so, please read the following two documents:

 2 Overview

We will create a simple application called "Colors", which will allow the user to convert the Red-, Green- and Blue-Values of a color into its hexadecimal value and vice versa. The development will proceed in the following steps:

  1. Project Definition/Specifications
  2. Project Design/Layout
  3. Resource Definitions
  4. Main Program
  5. Additional routines and procedures
  6. Makefile
  7. Testing and Debugging

These are only the main steps and everybody who has done any software development knows that a lot of other steps are involved, and that "Testing and Debugging" will take place at virtually every step of the project.

 3 "Colors" - A Palm Project Example

 3.1 Project Definition/Specifications

It's important to state the aim of the application at the beginning, no matter if you are writing the application for somebody else or just for fun. By thinking about what the program needs and how it is organized, what features the user might want, your project will get much more organized and thus easier to develop. These specifications also facilitate splitting up the project among different devlopers. Since this is a very simple example, you might think that this step is not neccessary, but let's do it anyway for educational purposes:

The application "Colors" will allow the user to convert the Red-, Green- and Blue-Values of a color into its hexadecimal value and vice versa. It should have a menubar containing the items "Edit" ("Cut", "Copy", "Paste") and "Options" ("About Colors"). Each command should have a graffiti shortcut character. By keeping the layout of our application close to other Palm-Applications, we ensure that the user feels familiar with it. We want to be able to cut, copy and paste from/to the clipboard by using the menu or the graffiti shortcuts; the other menu-item will pop up a small alert with useful (?) information about the program. When the user leaves the application, we want to store his/her latest result in a database and show it when the application is opened the next time. We also need a small error-alert in case the user enters invalid characters.

 3.2 Project Design/Layout

We now need to think about what the program interface will look like. From the specifications we know that we need the menu and editable text-fields for the Red-, Green, Blue-, and Hex-value of the color. We then want two buttons, one which will convert from hexadecimal to RGB and one which converts from RGB to hexadecimal. Note that there are of course many other ways of designing the program. Our interface might look something like this:

      Menu

            Red:           ___

            Green:         ___

            Blue:          ___

 

                From RGB     From Hex

 

            HexValue:      ___

    

We do not want a frame around the form, so we want to specify the size of the form to be 160x160.

 3.3 Resource Definitions

Designing the layout and then transferring this layout into code is very easy. All you need to do is create a text file containing the specifications, and a resource-header which will define the ID for each element. (You can have pilrc assign the ID's automatically, but it will be easier for you lateron in the code if you give a name to each element.) Here is what our "Colors.rcp" file will look like:

	#include "ColorsRsc.h"

	MENU ID ColorsMainMenuBar
	BEGIN
  	  PULLDOWN "Edit"
	  BEGIN
            MENUITEM "Cut   " ID Cut "X"
            MENUITEM "Copy   " ID Copy "C"
            MENUITEM "Paste   " ID Paste "P"
	  END
	  PULLDOWN "Options"
	  BEGIN
            MENUITEM "About Colors   " ID Info "A"
	  END
	END

	FORM ID ColorsForm AT (0 0 160 160)
	MENUID 1000
	BEGIN
	  TITLE "Palm Color Converter"
	  LABEL "Red:     " AUTOID AT (10 30)
	  FIELD ID ColorsFormFieldRed AT (75 PrevTop 15 15) RIGHTALIGN FONT 0 UNDERLINED SINGLELINE MAXCHARS 3 NUMERIC
	  LABEL "Green:   " AUTOID AT (10 PrevBottom+5)
	  FIELD ID ColorsFormFieldGreen AT (75 PrevTop 15 15) RIGHTALIGN FONT 0 UNDERLINED SINGLELINE MAXCHARS 3 NUMERIC
	  LABEL "Blue:    " AUTOID AT (10 PrevBottom+5)
	  FIELD ID ColorsFormFieldBlue AT (75 PrevTop 15 15) RIGHTALIGN FONT0 UNDERLINED SINGLELINE MAXCHARS 3 NUMERIC
	  BUTTON "From RGB" ID ColorsFormButtonFromRgb AT (20 PrevBottom+10 AUTO AUTO)
	  BUTTON "From Hex" ID ColorsFormButtonFromHex AT (PrevRight+20 PrevTop AUTO AUTO)
	  LABEL "Hexvalue:   " AUTOID AT (10 PrevBottom+20)
	  FIELD ID ColorsFormFieldHex AT (75 PrevTop 32 15) RIGHTALIGN FONT 0 UNDERLINED SINGLELINE MAXCHARS 6
	END

	ALERT ID AboutAlert
	INFORMATION
	BEGIN
	  TITLE "About Colors"
	  MESSAGE "The Palm Color Converter lets you convert the Red-, Green- 
                   and Blue values of a color into it's hexadecimal value and vice versa."
	  BUTTONS "OK"
	END

	ALERT ID TheError
	INFORMATION
	BEGIN
	  TITLE "Error"
	  MESSAGE "^1"
	  BUTTONS "OK"
	END
      

And that's all! The IDs are defined in the included headerfile "ColorsRsc.h":

	// ColorsRsc.h
	// defines constants for all the applications resources
	//
	// App Name: "Colors"
	//
	// App Version: "0.1"

	// Main Form
	#define ColorsForm                    1000

	// main menu
	#define ColorsMainMenuBar             1000
	#define Cut                           1001
	#define Copy                          1002
	#define Paste                         1003
	#define Info                          1004

	// fields and buttons
	#define ColorsFormFieldRed            1010
	#define ColorsFormFieldGreen          1011
	#define ColorsFormFieldBlue           1012
	#define ColorsFormButtonFromRgb       1013
	#define ColorsFormButtonFromHex       1014
	#define ColorsFormFieldHex            1015

	// Alerts
	#define AboutAlert                    2000
	#define TheError                      3000
      

As always, it's good practice to comment your code. Once you've written "Colors.rcp" and "ColorsRcp.h", you can preview your layout using pilrcui Colors.rcp:

 3.4 Main Program

Now it's time to write the main function (PilotMain), which is standard:

	DWord PilotMain(Word launchCode, Ptr cmdPBP, Word launchFlags) {
	  Err err = 0;

	  if (launchCode == sysAppLaunchCmdNormalLaunch) {
	    if ((err = StartApplication()) == 0) {
	      EventLoop();
	      StopApplication();
	    }
	  }
	  return err;
	}
      

For this simple application we only check if it is launched via the standard launch code, we ignore all other possible ways of launching the application. StartApplication is standard as well - it just calls OpenDatabase and calls FrmGotoForm(ColorsForm) to display our main (and only) form. OpenDatabase creates the database for our application if it doesn't exist, and creates record zero containing a null string. If all this took place without any errors, EventLoop is entered (which will wait for events triggered by the users actions) and is left only when interrupted by the systems call to stop the application. If this is the case, we call StopApplication which will write the necessary data into the database. You can review the complete code for "Colors" at the end of this document.

 3.5 Additional Routines and Procedures

In order for our form to process (handle) any incoming events, we need to write a Form Handler, which executes certain pieces of code according to the events we want to handle. Following is the function ColorsFormHandleEvent in parts:

	static Boolean ColorsFormHandleEvent(EventPtr event) {

	  //initialization of local variables

	  #ifdef __GNUC__
	  CALLBACK_PROLOGUE
	  #endif

	  index = 0;
	  theForm = FrmGetActiveForm();
	  switch (event->eType) {
	    case frmOpenEvent:
	      FrmDrawForm(theForm);
	      // here we also read the last record from the
	      // database and insert it into the appropriate field
	      handled = true;
              break;
	    case ctlSelectEvent:
	    // here we handle the two buttons
	    objID = event->data.ctlSelect.controlID;
	    if (objID == ColorsFormButtonFromRgb) {
              convertFromRgb();
	    }
	    else {
              convertFromHex();
	    }
	    handled = true;
	    break;
	    case menuEvent:
	      // here we handle the menu-events
	      handled = true;
	  }

	  #ifdef __GNUC__
	  CALLBACK_EPILOGUE
	  #endif

	  return handled;
	}
      

Note the inclusion of CALLBACK_PROLOGUE and CALLBACK_EPILOGUE. We need these since we are using GCC to compile the code. The GCC compiler's calling conventions differ from those in the Palm OS. In particular, the GCC compiler expects at startup that it can set up the A4 register (which it uses to access global variables) and that it will remain set throughout the life of the application. Unfortunately, this is not true when a GCC application calls a Palm OS routine that either directly or indirectly calls back to a GCC function. This can lead to spectacular applications crashes. Refer to the complete code at the end of this document to see what CALLBACK_PROLOGUE and CALLBACK_EPILOGUE do.

This function calls convertFromRgb or convertFromHex, the functions actually responsible for the conversion, depending on which button the user taps.

3.6 Makefile

Once we've written all the code, we need to compile and link it. This may not be very complicated for a small project like this, but for bigger projects this would be tedious work. I will not go into the details of Makefiles, see http://www.tw.gnu.org/software/make/make.html for further information. Here's the Makefile I used for our little project:

	APP             =Colors
	ICONTEXT        ="Colors"
	APPID           =WRLD
	RCP             =$(APP).rcp
	PRC             =$(APP).prc
	SRC             =$(APP).c
	GRC             =$(APP).grc
	BIN             =$(APP).bin
	
	CC              =m68k-palmos-coff-gcc
	PILRC           =pilrc
	TXT2BITM        =txt2bitm
	OBJRES          =m68k-palmos-coff-obj-res
	BUILDPRC        =build-prc

	CFLAGS          =-O0 -g $(DEFINES) $(INCLUDES)

	all:        clean    $(PRC)

	$(PRC):       grc.stamp bin.stamp;
            $(BUILDPRC) $(PRC) $(ICONTEXT) $(APPID) *.grc *.bin $(LINKFILES) 
            ls -l *.prc

	grc.stamp:    $(APP) ;
            $(OBJRES) $(APP)
            touch $@

	$(APP): $(SRC:.c=.o) ;
            $(CC) $(CFLAGS) $^ -o $@

	bin.stamp:    $(RCP) ;
            $(PILRC) $^ $(BINDIR)
            touch $@

	%.o: %.c ;
            $(CC) $(CFLAGS) -c $< -o $@

	clean:
           rm -rf *.o $(APP) *.bin *.grc *.prc *.stamp
      

Note that I always "make clean", since I split up the functions over several files. There are other (maybe more elegant) possibilities to do this with Makefiles and I won't try to argue that this method is better. Once compilation is complete, you will find the file "Colors.prc" in your directory - this is the application ready to install on your Palm (or POSE).

 4 The Code

Following is the code for the following files:

 4.1 Callbacks.h

 
#ifndef __CALLBACK_H__
#define __CALLBACK_H__

register void *reg_a4 asm("%a4");
#define CALLBACK_PROLOGUE \
   void *save_a4 = reg_a4; asm("move.l %%a5,%%a4; sub.l #edata,%%a4"
: :);
#define CALLBACK_EPILOGUE reg_a4 = save_a4;

#endif
      

 4.2 Colors.c


#include "Colors.h"
#include "Rgb2hex.c"
#include "Hex2rgb.c"
#include "EventHandlers.c"
#include "ColorsGeneral.c"

static void EventLoop(void) {
  EventType       event;
  Word            error;

  do {
    EvtGetEvent(&event, evtWaitForever);
    if (!SysHandleEvent(&event)) {
      if (!MenuHandleEvent(0, &event, &error)) {
        if (!ApplicationHandleEvent(&event)){
          FrmDispatchEvent(&event);
        }
      }
    }
  } while (event.eType != appStopEvent);
}

static Err OpenDatabase(void) {
  Err err = 0;
  UInt            index = 0;
  VoidHand        RecHandle;
  Ptr             RecPointer;
  char            nullstring = 0;

  myDB = DmOpenDatabaseByTypeCreator(myDBType, myAppID, dmModeReadWrite);
  if (!myDB) {
    if ((err = DmCreateDatabase(0, myDBName, myAppID, myDBType, false))!=0)
      return err;
    myDB = DmOpenDatabaseByTypeCreator(myDBType, myAppID, dmModeReadWrite);
    RecHandle = DmNewRecord(myDB, &index, 1);
    RecPointer = MemHandleLock(RecHandle);
    DmWrite(RecPointer, 0, &nullstring, 1);
    MemPtrUnlock(RecPointer);
    DmReleaseRecord(myDB, index, true);
  }
  return 0;
}

static Err StartApplication(void) {
  Err err = 0;
  err = OpenDatabase();
  if (err)
    return err;

  FrmGotoForm(ColorsForm);
  return 0;
}

static Err StopApplication(void) {
  VoidHand       myRecord;
  FormPtr        theForm;
  char           *theRecord;
  FieldPtr       theField;
  Ptr            RecPointer;
  UInt            index = 0;

  theForm = FrmGetActiveForm();
  theField = FrmGetObjectPtr(theForm, FrmGetObjectIndex (theForm, ColorsFormFieldHex));
  theRecord = theField->text;

  if (theRecord!=NULL) {
    DmRemoveRecord(myDB, index);
    myRecord = DmNewRecord(myDB, &index, 1);
    myRecord = DmResizeRecord(myDB, index, 6);
    RecPointer = MemHandleLock(myRecord);
    DmWrite(RecPointer, 0, theRecord, 6);
    MemPtrUnlock(RecPointer);
    DmReleaseRecord(myDB, index, true);
  }
  DmCloseDatabase(myDB);
}

DWord PilotMain(Word launchCode, Ptr cmdPBP, Word launchFlags) {
  Err err = 0;

  if (launchCode == sysAppLaunchCmdNormalLaunch) {
    if ((err = StartApplication()) == 0) {
      EventLoop();
      StopApplication();
    }
  }
  return err;
}
      

 4.3 Colors.h

 
#ifndef __COLORS_H
#define __COLORS_H

#include <Pilot.h>
#include <stdio.h>
#include <stdlib.h>
#include "ColorsRsc.h"

#ifdef __GNUC__
#include "Callbacks.h"
#endif

#define myAppID             'CLRS'
#define myDBType            'Data'

DmOpenRef         myDB;
char              myDBName[] = "ColorsDB";

static int myIsXDigit(char);
static void convertFromHex(void);
static void AlertError(char *);
static Boolean ColorsFormHandleEvent(EventPtr);
static Boolean ApplicationHandleEvent(EventPtr);
static void EventLoop(void);
static Err OpenDatabase(void);
static Err StartApplication(void);
static Err StopApplication(void);
DWord PilotMain(Word launchCode, Ptr cmdPBP, Word launchFlags);
static void convertFromRgb(void);
 
#endif
      

 4.4 Colors.rcp

See Section 3.3

 4.5 ColorsGeneral.c


#include "Colors.h"

static void AlertError(char *theMessage) {
  FrmCustomAlert(TheError, theMessage, NULL, NULL);
}
      

 4.6 ColorsRsc.h

See Section 3.3

 4.7 EventHandlers.c


#include "Colors.h"

static Boolean ColorsFormHandleEvent(EventPtr event) {
  Boolean         handled = false;
  int             objID;
  FormPtr         theForm;
  FieldPtr        theField;
  VoidHand        myRecord;
  UInt            index;
  Ptr             RecPointer;
  char            *theRecord;

#ifdef __GNUC__
  CALLBACK_PROLOGUE
#endif

    index = 0;
    theForm = FrmGetActiveForm();
    switch (event->eType) {
    case frmOpenEvent:
      // get the record
      myRecord = DmGetRecord(myDB, index);
      theRecord = MemHandleLock(myRecord);
      MemHandleUnlock(myRecord);
      DmReleaseRecord(myDB, index, true);  
      FrmDrawForm(theForm);
      if ((theRecord != NULL)&&(StrLen(theRecord)==6)) {
        // we have a valid record, display it
        FldInsert(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldHex)), theRecord, 6);
        // and show the rgb  - this way we don't need to store those!
        convertFromHex();
      }
      handled = true;
      break;
    case ctlSelectEvent:
      objID = event->data.ctlSelect.controlID;
      if (objID == ColorsFormButtonFromRgb) {
        convertFromRgb();
      }
      else {
        convertFromHex();
      }
      handled = true;
      break;
    case menuEvent:
      if (event->data.menu.itemID == Cut) {
        // Cut
        objID = FrmGetFocus(theForm);
        FldCut(FrmGetObjectPtr(theForm, objID));
      }
      else if (event->data.menu.itemID == Copy) {
        // Copy
        objID = FrmGetFocus(theForm);
        FldCopy(FrmGetObjectPtr(theForm, objID));
        break;
      }
      else if (event->data.menu.itemID == Paste) {
        // Paste
        objID = FrmGetFocus(theForm);
        FldPaste(FrmGetObjectPtr(theForm, objID));
        break;
      }
      else if (event->data.menu.itemID == Info)
        // display info
        FrmAlert(AboutAlert);
      handled = true;
    }
  
#ifdef __GNUC__
  CALLBACK_EPILOGUE
#endif
  
    return handled;
}
 

static Boolean ApplicationHandleEvent(EventPtr event) {
  FormPtr         frm;
  Int             formId;
  Boolean         handled = false;
 
  if (event->eType == frmLoadEvent) {
    // Load the form resource specified in the event, then activate
    formId = event->data.frmLoad.formID;
    frm = FrmInitForm(formId);
    FrmSetActiveForm(frm);
    
    // Set the event Handler for the form. The handler of the currently active form
    // is called by FrmDispatchEvent each time it is called
    switch (formId) {
    case ColorsForm:
      FrmSetEventHandler(frm, ColorsFormHandleEvent);
      break;
    }
    handled = true;
  }
  return handled;
}
      

 4.8 Hex2rgb.c

 
#include "Colors.h"

static int myIsXDigit(char c) {
  if ((c=='0')||(c==NULL))
    return 0;
  if (c=='1')
    return 1;
  if (c=='2')
    return 2;
  if (c=='3')
    return 3;
  if (c=='4')
    return 4;
  if (c=='5')
    return 5;
  if (c=='6')
    return 6;
  if (c=='7')
    return 7;
  if (c=='8')
    return 8;
  if (c=='9')
    return 9;
  if ((c=='a')||(c=='A'))
    return 10;
  if ((c=='b')||(c=='B'))
    return 11;
  if ((c=='c')||(c=='C'))
    return 12;
  if ((c=='d')||(c=='D'))
    return 13;
  if ((c=='e')||(c=='E'))
    return 14;
  if ((c=='f')||(c=='F'))
    return 15;
  return -1;
}
 
 
static void convertFromHex(void) {
  int             i;
  char            *FldHex;
  int             flag=0;
  FormPtr         theForm;
  char            hex[6] = {'0','0','0','0','0','0'};
  int             tmp[3] = {0,0,0};
 
  theForm = FrmGetActiveForm();
 
  // delete old stuff
  FldDelete(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldRed)), 0, 3);
  FldDelete(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldGreen)), 0, 3);
  FldDelete(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldBlue)), 0, 3);
 
  // get input
  FldHex = FldGetTextPtr(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldHex)));
 
  // check if input is correct
  if (FldHex!=NULL) {
    for (i=0; i<6; i++) {
      // check if input is correct
      if (!flag){
        hex[i] = FldHex[i];
        if (myIsXDigit(hex[i])<0) {
          AlertError("Incorrect Input!");
          flag = 1;
        }
      }
    }
    if (StrLen(hex)<6) {
      AlertError("Not enough characters!");
      flag = 1;
    }
    if (!flag) {
      for (i=0;i<3;i++) {
        tmp[i] = 0;
        tmp[i] += myIsXDigit(hex[i*2])*16;
        tmp[i] += myIsXDigit(hex[i*2+1]);
      }
    }
  }
  else {
    for (i=0;i<3;i++) 
      tmp[i]=0;
  }
  if (!flag) {
    FldInsert(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldRed)), StrIToA(hex,tmp[0]), StrLen(StrIToA(hex, tmp[0])));
    FldInsert(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldGreen)), StrIToA(hex,tmp[1]), StrLen(StrIToA(hex, tmp[1])));
    FldInsert(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldBlue)), StrIToA(hex,tmp[2]), StrLen(StrIToA(hex, tmp[2])));
  }
}
      

Note that the much more elegant way to convert the value in plain C as shown below will not work, as the printf -functions are implemented in a different way, just as filestreams are not as easy as in plain C.

 4.8.1 Plain C code for hex2rgb

 
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
 
char *color[] = { "Red:", "Green:", "Blue:" };
 
void error( const char *str ) {
    fprintf(stderr, "%s\n", str);
    exit(1);
}
 
int main(int argc, char *argv[]) {
  int i;
  unsigned long l;
  char *input = argv[1];
 
  if(argc != 2)
    error("Usage: hex2rgb xxxxxx, where x=[a-fA-F0-9]");
  if(strlen(input) != 6)
    error("The hexadecimal value of a color must have 6 characters!");
  for( i = 0; i < strlen( input ); i++ )
    if( !isxdigit( input[i] ) )
      error( "Input is not a valid hexnumber" );
 
  i = sscanf(input, "%6lx", &l);
 
  if(i <= 0)
    error("Error in hexvalue!");
 
  for(i = 0; i<3; i++) {
    fprintf(stdout,  "%d ", (int)( l & 0xFF0000 ) >> 16 );
    l <<= 8;
  }
  fprintf(stdout, "\n");
 
  return 0;
}
	
 4.9 Makefile

See Section 3.6

 4.10 Rgb2hex

 

#include "Colors.h"

static void convertFromRgb(void) {
  char            *FldRed;
  char            *FldGreen;
  char            *FldBlue;
  int             flag=0;
  FormPtr         theForm;
  char            tmp[8];
  char            result[6] = {'0','0','0','0','0','0'};

  theForm = FrmGetActiveForm();
 
  // delete old entry
  FldDelete(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldHex)), 0, 6);
 
  // get the input
  FldRed = FldGetTextPtr(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldRed)));
  FldGreen = FldGetTextPtr(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldGreen)));
  FldBlue = FldGetTextPtr(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldBlue)));
 
  //  check if input is correct
  if ((FldRed!=NULL)&&(atoi(FldRed)>255)) {
    AlertError("Incorrect input!");
    flag = 1;
  }
  if ((FldGreen!=NULL)&&(atoi(FldGreen)>255)) {
    AlertError("Incorrect input!");
    flag = 1;
  }
  if ((FldBlue!=NULL)&&(atoi(FldBlue)>255)) {
      AlertError("Incorrect input!");
      flag = 1;
  }
 
  // all's swell, we go on
  if (!flag) {
    if (FldRed == NULL) {
      result[0] = '0';
      result[1] = '0';
    }
    else {
      StrIToH(tmp, atoi(FldRed));
      result[0] = tmp[6];
      result[1] = tmp[7];
    }
    if (FldGreen == NULL) {
      result[2] = '0';
      result[3] = '0';
    }
    else {
      StrIToH(tmp, atoi(FldGreen));
      result[2] = tmp[6];
      result[3] = tmp[7];
    }
    if (FldBlue == NULL) {
      result[4] = '0';
      result[5] = '0';
    }
    else {
      StrIToH(tmp, atoi(FldBlue));
      result[4] = tmp[6];
      result[5] = tmp[7];
    }
    result[6] = '\0';
    // write the correct hexvalue into the appropriate field
    FldInsert(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldHex)), result, 6);
  }
}

      

As before, you would implement this function in a slightly different way if using plain C:

 4.10.1 Plain C code for rgb2hex


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
		
void error( const char *str ) {
    fprintf( stderr, "%s\n", str );
    exit(1);
}
int main(int argc, char **argv) {
  char *input[3];
  int i;
 
  if (argc!=4) {
    error("Usage: rgb2hex R G B (where  0 >= R,G,B (integer) <= 255)");
    exit(1);
  }
  for (i=0; i<3; i++) {
    input[i] = argv[i+1];
  }
  for (i=0; i<strlen(input[0]); i++) {
    if (!isdigit(input[0][i])) {
      error("Incorrect input!");
      exit(1);
    }
  }
  for (i=0; i<strlen(input[1]); i++) {
    if (!isdigit(input[1][i])) {
      error("Incorrect input!");
      exit(1);
    }
  if ((atoi(input[0])>255)||(atoi(input[1])>255)||(atoi(input[2])>255)) {
    error("Incorret input!");
    exit(1);
  }
 
  fprintf(stdout,"%02x%02x%02x\n", atoi(input[0]), atoi(input[1]), atoi(input[2]));
}

 5 Screenshot

If you have any questions or find that the information provided in this document is wrong or ambiguous, please email me at jschauma@netmeister.org.