C Reaction example: HelloWorld

  • up
    33%
  • down
    67%

Here is a simple "hello world" program written to show how to create a small Reaction UI:

Includes
Include description pending

  1. #include <classes/window.h>
  2. #include <proto/exec.h>
  3. #include <proto/intuition.h>
  4. #include <proto/dos.h>
  5. #include <proto/window.h>
  6. #include <proto/layout.h>
  7. #include <proto/label.h>
  8. #include <proto/string.h>
  9. #include <gadgets/string.h>
  10. #include <images/label.h>
  11. #include <reaction/reaction_macros.h>

Enum
We use an enum to help us keep track of our GUI objects/gadgets.

Here we will use GID_MAIN for a vertical group object that will contain the other objects, GID_STRING_WORLD for the string object and GID_BUTTON_BYE for the button object.
We add a GID_LAST at the bottom of the enum to be able to use that enum as the size of our array of gadgets.

  1. enum
  2. {
  3. GID_MAIN=0,
  4. GID_STRING_WORLD,
  5. GID_BUTTON_BYE,
  6. GID_LAST
  7. };

Main function

  1. // Login GUI
  2. int main(void)
  3. {

Local variables
To start with we set up the local variables we are going to use for our GUI.
We need a message port (AppPort) to be able to get the events that the UI is going to send when we interact with the window. We also need a window (window) and space for all the gadgets (gadgets) we want to do things with. We also have a variable to store the object (object) we use to create the window.
Then we have som variable for signal handling (wait, signal), somewhere to store results and codes (result, code) and finally a variable for terminating (done) our program loop.

  1. struct MsgPort *AppPort = NULL;
  2. struct Window *window = NULL;
  3. struct Gadget *gadgets[GID_LAST];
  4. Object *object = NULL;
  5.  
  6. ULONG wait, signal;
  7. ULONG result;
  8. UWORD code;
  9.  
  10. int done = FALSE;

Create Message port
To be able to get signals and communicate with the window we need to set up a message port that we will attach to the window.

  1. if ((AppPort = (struct MsgPort *)IExec->CreateMsgPort()) == NULL)
  2. return 0;

Create Window object
Code description pending

  1. object = (Object *)WindowObject,
  2. WA_Title, "Hello world!",
  3. WA_Activate, TRUE,
  4. WA_DepthGadget, TRUE,
  5. WA_DragBar, TRUE,
  6. WA_CloseGadget, TRUE,
  7. WA_SizeGadget, FALSE,
  8. WINDOW_IconifyGadget, TRUE,
  9. WINDOW_IconTitle, "Hello world",
  10. WINDOW_AppPort, AppPort,
  11. WINDOW_Position, WPOS_CENTERMOUSE,
  12. WINDOW_ParentGroup, gadgets[GID_MAIN] = (struct Gadget *)VGroupObject,
  13. LAYOUT_AddChild, gadgets[GID_STRING_WORLD] = (struct Gadget *)StringObject,
  14. GA_ID, GID_STRING_WORLD,
  15. STRINGA_TextVal, "world!",
  16. GA_TabCycle, TRUE,
  17. GA_ReadOnly, TRUE,
  18. EndObject,
  19. CHILD_Label, LabelObject, LABEL_Text, "Hello", LabelEnd,
  20.  
  21. LAYOUT_AddChild, HGroupObject,
  22. LAYOUT_AddChild, gadgets[GID_BUTTON_BYE] = ButtonObject,
  23. GA_ID, GID_BUTTON_BYE,
  24. GA_Text, "Bye!",
  25. GA_RelVerify, TRUE,
  26. GA_TabCycle, TRUE,
  27. EndObject,
  28.  
  29. EndGroup,
  30. EndGroup,
  31. EndWindow;
  32.  
  33. if (object == NULL)
  34. {
  35. IExec->DeletePort(AppPort);
  36. return 0;
  37. }

Open window
Code description pending

  1. if ((window = (struct Window *) RA_OpenWindow(object)) == NULL)
  2. {
  3. IIntuition->DisposeObject(object);
  4. IExec->DeletePort(AppPort);
  5. return 0;
  6. }

Signal mask
Code description pending

  1. IIntuition->GetAttr(WINDOW_SigMask, object, &signal);

Main loop
This is the main loop.

  1. while (!done)
  2. {

Wait for signal
The exec library has a function for waiting for signals. Earlier we set up the signal mask variable named signal with the signals that the window will send. Here we also add the SIGBREAKF_CTRL_C to the mask so we can listen for that signal too.
The program will halt at the Wait call until a signal is received.
If the signal we got from the Wait function is a SIGBREAKF_CTRL_C we end the loop by setting done to TRUE.

  1. wait = IExec->Wait( signal | SIGBREAKF_CTRL_C );
  2.  
  3. if ( wait & SIGBREAKF_CTRL_C )
  4. done = TRUE;
  5. else
  6. {

Inputs from the GUI
Code description pending

  1. while ( (result = RA_HandleInput(object, &code) ) != WMHI_LASTMSG )
  2. {
  3. switch (result & WMHI_CLASSMASK)
  4. {
  5. case WMHI_CLOSEWINDOW:
  6. done = TRUE;
  7. break;
  8. case WMHI_ICONIFY:
  9. RA_Iconify(object);
  10. window = NULL;
  11. break;
  12. case WMHI_UNICONIFY:
  13. window = (struct Window *) RA_OpenWindow(object);
  14.  
  15. if (window)
  16. IIntuition->GetAttr(WINDOW_SigMask, object, &signal);
  17. else
  18. done = TRUE; // error re-opening window!
  19. break;
  20. case WMHI_GADGETUP:
  21. switch (result & WMHI_GADGETMASK)
  22. {
  23. case GID_BUTTON_BYE:
  24. done = TRUE;
  25. break;
  26. default:;
  27. }
  28. break;
  29. default:;
  30. }
  31. }
  32. }
  33. }

Clean up
When we are done with the GUI we need to give back everything we allocated and created. So we dispose the window object. That also closes the window. Then we delete the message port. After that we can end the program.

  1. IIntuition->DisposeObject(object);
  2. IExec->DeletePort(AppPort);
  3. return 0;
  4. }

Compilation
The code is provided in this blog as a .txt file (.c is not allowed to be added to the blogs).
Rename the file to helloworld.c and compile with:
gcc -o helloworld helloworld.c -lraauto -lauto

If compilation went well you can run the helloworld program.

Final notes
Please provide constructive criticism how to improve/correct this example program in a useful way to help beginners getting started with Reaction programming.

There also is a good tutorial here.

Tags: 

Blog post type: 

AttachmentSize
Plain text icon helloworld.txt3.11 KB

Comments

kas1e's picture

As i complete noob with reaction, i see that need to add a bit more description to source code itself and just a bit of explaining here and here. I.e. something like this:


For the making a simply reaction-based window, you need:

1. Have right protos (that and that for that, that and that for that)
2. Make main object with such layout and with such groups/childs/etc
3. You need "enum" because of blabla
4. Create ports (Because they need for blalba)
5. Handle signals because you need blalbal

The way of working with reaction different with way how for example you works with MUI, you need there that and that, because of that. While you can that and that, because of that.
....

Something like this will be interesting and helpfull as well imho.

Menthos's picture

Thanks kas1e for your suggestions.

I will try to find the time to split the code into sections and describing each section (what it does and why).

I am also hoping that someone will look at the code and comment on the way it is set up. If the code is good or that it do things the 'wrong' way.

/M.Andersson, Viking Technology

YesCop's picture

Hi Menthos,

As Kas1e, I am not fond of amiga programming so I would like to thank you for your idea.
For me, what I would understand is the connection, the link between all objects in reaction; How is Reaction working to understand better the system ? and so programming it better.

Just another idea, could you end your post with the complete source without the line numbers ?
Thanks
YesCop

Menthos's picture

I am thinking to attach the code as a file to the blog.

/M.Andersson, Viking Technology

Menthos's picture

I have added the code as an attachment to the blog and have added some descriptions.

Descriptions of the trickier parts are still pending.

/M.Andersson, Viking Technology

YesCop's picture

Hi Menthos,

I forgot to say that I like your avatar.
Let's work now.

I tested your program. I used to use C++ so your file has been renamed helloworld.cc.

I had a compliation error. Here are the results.

gcc -o helloworld helloworld.c -lraauto -lauto
helloworld.c: In function 'main':
helloworld.c:64: warning: assignment from incompatible pointer type


gcc -o helloworld helloworld.cc -lraauto -lauto
helloworld.cc: In function 'int main()':
helloworld.cc:64: error: cannot convert 'Object*' to 'Gadget*' in assignment


g++ -o helloworld helloworld.cc -lraauto -lauto
helloworld.cc: In function 'int main()':
helloworld.cc:64: error: cannot convert 'Object*' to 'Gadget*' in assignment

It is strange in the first version, the error became a warning and I could launch your program.
Any ideas to correct that error ? I don't like C much, I prefer C++.

Bye
YesCop

Menthos's picture

I think it can be because the button was not cast to a gadget.

Try this: (struct Gadget *)ButtonObject

/M.Andersson, Viking Technology

trixie's picture

As of the latest AmigaOS4 SDK, BOOPSI gadgets and images are defined as pointers to objects, not structures Gadget and Image, respectively. So your gadget definition above should read

Object *gadgets[GID_LAST];

and not

struct Gadget *gadgets[GID_LAST];

Should you then need to use gadget-specific functions such as SetGadgetAttrs() you will cast the variable to a struct Gadget pointer:

IIntuition->SetGadgetAttrs( (struct Gadget *) gadgets[GID_STRING_WORLD], window, NULL, ... );

AmigaOne X5000-020 / 2GB RAM / Sapphire Pulse Radeon RX 560 / AmigaOS 4.1 Final Edition Update 2

Menthos's picture

Thanks Trixie!

I will do this update too when I find the time (I have not started my Amiga in about a month :( ).

/M.Andersson, Viking Technology

salass00's picture

You should use DeletePort() only for ports created with CreatePort() and DeleteMsgPort() for ports created with CreateMsgPort().

Better yet since your code is for OS4.x only you should use the new AllocSysObject()/FreeSysObject() functions instead as the other methods are now obsolete.

  1. AppPort = IExec->AllocSysObject(ASOT_PORT, NULL);
  2. if (AppPort != NULL) {
  3. /* ... */
  4. IExec->FreeSysObject(ASOT_PORT, AppPort);
  5. }
Menthos's picture

Thanks!

I will try to update the code this weekend and will update the example after that.

Everybody: Keep the improvement coming so we have a really good and up to date example!

/M.Andersson, Viking Technology