Adding a console input to an application

12 posts / 0 new
Last post
JosDuchIt
JosDuchIt's picture
Offline
Last seen: 8 years 1 week ago
Joined: 2012-01-08 20:57
Adding a console input to an application

Gui4Cli has a debug output console.
I would like to use that for input too: a line should execute a given Gui4Cli commandline
This would allow to test those commandlines, to load or open gui's to do in fact anything that could be done from a gadget's command sequence.
How could this be done? A pseudo-code sketch would be much appreciated

hypex
hypex's picture
Offline
Last seen: 3 months 3 weeks ago
Joined: 2011-09-09 16:20
It depends how it is opened.

It depends how it is opened. If you can access it have you tried reading from it? There are functions like WaitForChar() to test for characters and then you could Read() or FGetC() from it.

JosDuchIt
JosDuchIt's picture
Offline
Last seen: 8 years 1 week ago
Joined: 2012-01-08 20:57
Gui4Cli opens a lot of

Gui4Cli opens a lot of windows (guis) the user wants to manipulate.
Errors, debug info prograamed output are all directed to an outpu console, opened with the first output that needs it.

In main() this console seems to be 'opened' but it won't appear if not in debug mode

  1. // *------------------- Get default console ---------------------* /
  2.  
  3. if (DEFcp == ZERO) // if no default console
  4. {
  5. if (DEFcp = Open (Defconsolespec, MODE_OLDFILE))
  6. { SelectOutput (DEFcp);
  7. DEFcpOPEN = 1;
  8. }
  9. else
  10. myerror (NULL, 1999, 0, "No console");
  11. }

later the main loop looks like

  1. while (!stop)
  2. {
  3.  
  4. CURRENT_GF = NULL;
  5. MAINsignal = Wait (ALLsignals);
  6.  
  7. // -----------------------------------------------------------------------
  8. // ================================ REXX PORT ============================
  9. // -----------------------------------------------------------------------
  10. if (MAINsignal & myrxportsig)
  11.  
  12. // -----------------------------------------------------------------------
  13. // ============================ Sound PORT ===============================
  14. // -----------------------------------------------------------------------
  15. else if (MAINsignal & soundsig) HandleSoundPort ();
  16.  
  17. // -----------------------------------------------------------------------
  18. // ============================ Commodities PORT =========================
  19. // -----------------------------------------------------------------------
  20.  
  21. else if (MAINsignal & cxsig)
  22.  
  23.  
  24. // -----------------------------------------------------------------------
  25. // ============================ AppMessage PORT ==========================
  26. // -----------------------------------------------------------------------
  27.  
  28. else if (MAINsignal & mywbportsig) // * APPMESSAGE * /
  29.  
  30.  
  31. // -----------------------------------------------------------------------
  32. // ============================ Reaction PORT ============================
  33. // -----------------------------------------------------------------------
  34.  
  35.  
  36. else if (MAINsignal & myRAportsig) stop = HandleReaction (); //
  37.  
  38. // -----------------------------------------------------------------------
  39. // ============================= Window PORT =============================
  40. // -----------------------------------------------------------------------
  41.  
  42. else if (MAINsignal & mywnportsig)
  43. {
  44. while ((!stop) && (wnmsg = GT_GetIMsg(mywnport)))
  45. {
  46. Treatment of messages to 'normal' window
  47.  
  48. } // * end of while getmessage for gadgets * /
  49. } // * end of else on window port * /
  50.  
  51. } // * end of main while * /

I guess the console window must for input be treated in this loop. It must have the focus and be recognised as such. But that's me just repeating what i did read. I guess you can't put in the WaitForChar() just like that in the loop?

For output the print statements are all over the place as expected

thomas
thomas's picture
Offline
Last seen: 3 months 1 week ago
Joined: 2011-05-16 14:23
You could use WaitForChar

You could use WaitForChar asynchronously, i.e. send an ACTION_WAIT_CHAR to the handler and add the replyport to your ALLsignals.

JosDuchIt
JosDuchIt's picture
Offline
Last seen: 8 years 1 week ago
Joined: 2012-01-08 20:57
@Thomas, I am not the author

@Thomas,
I am not the author of Gui4Cli, and still a lousy C-programmer with too little ready knowledge of all the system libraries used. So the impressive list of ports opened does not reflect the level o my understanding.
I did read chapters 9 & 4 of the RKRM , looked at WaitForChar, but i am still lost a bit, confused with stdin & stdout versus the console device as documented in chapter 4 and the use of the main while loop and uyour solution with ACTION_WAIT_CHAR and adding a replyport toe Allsignals

Based on what i understand, the solution i can see is as follows 
- The code opening the console above and containing:

  1. { SelectOutput (DEFcp);
  2. DEFcpOPEN = 1;
  3. }

has to be completed for input

  1. {
  2. SelectOutput (DEFcp);
  3. SelectInput (DEFcp);
  4. DEFcpOPEN = 1;
  5. }

If seems that in the main loop a second one is needed
that you can enter and leave (switch between gui-inputmode and console-inputmode
- you enter when the console get the focus and leave when another gui gets it
- or you enter and leave it using the Gui4Cli appmenu

In this second loop Gui4Cli writes a prompt, waits for and reads the user's commandline (can be the launch of a commandsequence in one of the loaded gui's, print the results (can be a debugging output corresponding to the mentioned commandsequnce)

In a simple no gui program you could use here sscanf and printf functions, so probably you can use those here too (as well as OS4 system I/O and WaitForChar) ?
Thanks for your help

thomas
thomas's picture
Offline
Last seen: 3 months 1 week ago
Joined: 2011-05-16 14:23
One major drawback is that

One major drawback is that the console window opens whenever somebody accesses it. The call to WaitForChar is one such access which means that the console window opens immediately and you can never close it.

Here is a working example:

  1. /* Handle a console window and an intuition window at the same time */
  2.  
  3. /*----------------------------------------------------------------------------*/
  4. /* System includes */
  5. /*----------------------------------------------------------------------------*/
  6.  
  7. #include <proto/exec.h>
  8. #include <proto/dos.h>
  9. #include <proto/intuition.h>
  10.  
  11. /*----------------------------------------------------------------------------*/
  12. /* Start asyncronous WaitForChar */
  13. /*----------------------------------------------------------------------------*/
  14.  
  15. struct MsgPort *startWaitForChar (BPTR file,LONG timeout,struct MsgPort *replyport)
  16.  
  17. {
  18. struct FileHandle *fh = BADDR(file);
  19. struct DosPacket *packet;
  20.  
  21. if (fh && fh->fh_Type)
  22. {
  23. if (packet = AllocDosObject (DOS_STDPKT,TAG_END))
  24. {
  25. packet->dp_Port = replyport;
  26.  
  27. packet->dp_Type = ACTION_WAIT_CHAR;
  28. packet->dp_Arg1 = timeout;
  29.  
  30. PutMsg (fh->fh_Type,packet->dp_Link);
  31. return (replyport);
  32. }
  33. }
  34.  
  35. return (NULL);
  36. }
  37.  
  38. /*----------------------------------------------------------------------------*/
  39. /* Wait for asyncronous read/write to complete and fetch the result */
  40. /*----------------------------------------------------------------------------*/
  41.  
  42. LONG WaitDosIO (struct MsgPort *port)
  43.  
  44. {
  45. struct Message *msg;
  46. struct DosPacket *packet;
  47. long rc;
  48.  
  49. WaitPort (port);
  50. msg = GetMsg (port);
  51. packet = (struct DosPacket *) msg->mn_Node.ln_Name;
  52. rc = packet->dp_Res1;
  53. FreeDosObject (DOS_STDPKT,packet);
  54.  
  55. return (rc);
  56. }
  57.  
  58. /*----------------------------------------------------------------------------*/
  59. /* Main program */
  60. /*----------------------------------------------------------------------------*/
  61.  
  62. int main (void)
  63.  
  64. {
  65. struct MsgPort *conport = CreateMsgPort();
  66.  
  67. if (conport)
  68. {
  69. BPTR con = Open ("CON:0/20/300/200/Console/AUTO/CLOSE/INACTIVE",MODE_NEWFILE);
  70.  
  71. if (con)
  72. {
  73. struct Window *win = OpenWindowTags (NULL,
  74. WA_Left,308,
  75. WA_Top,20,
  76. WA_Width,300,
  77. WA_Height,200,
  78. WA_Title,"Intuition Window",
  79. WA_Flags,WFLG_CLOSEGADGET | WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_SIZEGADGET | WFLG_ACTIVATE | WFLG_NOCAREREFRESH,
  80. WA_IDCMP,IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | IDCMP_NEWSIZE,
  81. WA_MinWidth,80,
  82. WA_MinHeight,80,
  83. WA_MaxWidth,0x7fff,
  84. WA_MaxHeight,0x7fff,
  85. TAG_END);
  86.  
  87. if (win)
  88. {
  89. BOOL cont = TRUE;
  90.  
  91. startWaitForChar (con,100000,conport);
  92.  
  93. do {
  94. ULONG consig = 1L << conport->mp_SigBit;
  95. ULONG winsig = 1L << win->UserPort->mp_SigBit;
  96.  
  97. ULONG sigrecvd = Wait (consig | winsig | SIGBREAKF_CTRL_C);
  98.  
  99. if (sigrecvd & SIGBREAKF_CTRL_C)
  100. cont = FALSE;
  101.  
  102. if (sigrecvd & winsig)
  103. {
  104. struct IntuiMessage *imsg;
  105.  
  106. while (imsg = (struct IntuiMessage *) GetMsg (win->UserPort))
  107. {
  108. switch (imsg->Class)
  109. {
  110. case IDCMP_VANILLAKEY:
  111. if (imsg->Code == 0x1b)
  112. cont = FALSE;
  113. else
  114. FPrintf (con,"Key pressed: <%lc>\n",imsg->Code);
  115. break;
  116. case IDCMP_CLOSEWINDOW:
  117. cont = FALSE;
  118. break;
  119. }
  120. ReplyMsg ((struct Message *) imsg);
  121. }
  122. }
  123.  
  124. if (sigrecvd & consig)
  125. {
  126. if (WaitDosIO (conport))
  127. {
  128. char buffer[80];
  129. int len = Read (con,buffer,79);
  130. if (len < 0) len = 0;
  131. buffer[len] = 0;
  132. FPrintf (con,"Input from console: <%s>\n",buffer);
  133. }
  134.  
  135. startWaitForChar (con,100000,conport);
  136. }
  137. }
  138. while (cont);
  139.  
  140. CloseWindow (win);
  141.  
  142. WaitDosIO (conport);
  143. }
  144.  
  145. Close (con);
  146. }
  147.  
  148. DeleteMsgPort (conport);
  149. }
  150.  
  151. return (0);
  152. }
  153.  
  154. /*----------------------------------------------------------------------------*/
  155. /* End of source code */
  156. /*----------------------------------------------------------------------------*/

Please don't fiddle around with SelectInput and SelectOutput if you don't understand what they do.
Especially calling one of them without remembering the return value is dangerous. What does your cleanup routine do if it finds DEFcpOPEN == 1 ?

JosDuchIt
JosDuchIt's picture
Offline
Last seen: 8 years 1 week ago
Joined: 2012-01-08 20:57
@Thomas Thank you very much

@Thomas

Thank you very much for the example, and your warning

What does your cleanup routine do if it finds DEFcpOPEN == 1 ?

just this for now

  1. if (DEFcpOPEN) Close (DEFcp); //console
thomas
thomas's picture
Offline
Last seen: 3 months 1 week ago
Joined: 2011-05-16 14:23
Did you never get a "file

Did you never get a "file handle closed twice" guru?

The handle you supply to SelectOutput must not be closed. You have to reset it to the old handle before you may close it.

So in the beginning it should say

  1. DEFcp = Open (whatever,MODE_NEWFILE);
  2.  
  3. if (DEFcp)
  4. {
  5. old_output = SelectOutput (DEFcp;
  6. old_input = SelectInput (DEFcp);
  7. }

and in the end of the program

  1. if (DEFcp)
  2. {
  3. SelectOutput (old_output);
  4. SelectInput (old_input);
  5. Close (DEFcp);
  6. }

DEFcpOPEN is not needed at all because DEFcpOPEN != 0 is equivalent to DEFcp != NULL.

SelectOutput() sets the value which is returned by Output() and which is used by Printf() and the like. SelectInput() sets the value which is returned by Input(). Between SelectOutput(your_window) and SelectOutput(old_output) your_window is in use by the system and may under no circumstances be closed. The same applies to SelectInput(your_window) and SelectInput(old_input).

JosDuchIt
JosDuchIt's picture
Offline
Last seen: 8 years 1 week ago
Joined: 2012-01-08 20:57
Did you never get a "file

Did you never get a "file handle closed twice" guru?

Till now never, but i don't have much experience with the console being used for input too. on the other hand i don't see clearly why i should experience it more in this case. DEFcp is the only console handle opened and in principle it remains open till cleanup. (There are cases where it will be closed and reopened with a new consolespec (eg with the GUI4CLI "SET OUTPUT CON:...." command)
I do not see however why introducing

  1. SelectOutput (old_output);
  2. SelectInput (old_input);

in the closing sequence would prevent a double closure: they don't modify the handle DEFcp that has to be closed, and they ar not conditionals ?
If i stick to having only one console open, at most, at any time, i must be safe ?

DEFcpOPEN is not needed at all because DEFcpOPEN != 0 is equivalent to DEFcp != NULL.
Seems right to me.

Thanks for your detailed and insightfull comments.

thomas
thomas's picture
Offline
Last seen: 3 months 1 week ago
Joined: 2011-05-16 14:23
DEFcp is the only console

DEFcp is the only console handle opened and in principle it remains open till cleanup.

But you do not clean up. That's the problem. With SelectOutput(DEFcp) you tell the Shell that DEFcp is the output window. When your program quits you Close(DEFcp) but the Shell still thinks it is the output window. So if the Shell tries to write something out after your program has ended, it will crash because it tries to access DEFcp which you already closed. And if the Shell ends after your program, it will receive the "handle closed twice" guru.

Each SelectOutput(something) must be complemented by a SelectOutput(oldvalue) and each SelectInput(something) must be complemented by a SelectInput(oldvalue).

JosDuchIt
JosDuchIt's picture
Offline
Last seen: 8 years 1 week ago
Joined: 2012-01-08 20:57
With SelectOutput(DEFcp) you

With SelectOutput(DEFcp) you tell the Shell that DEFcp is the output window. When your program quits you Close(DEFcp) but the Shell still thinks it is the output window. So if the Shell tries to write something out after your program has ended, it will crash because it tries to access DEFcp which you already closed.

There are indeed a number of cases that i have to consider ( I am working with Gui4 Cli on a dayly base nearly ten years now, never having had a problem of "double closing" or other, but i prefer to walk on safe ground: i may have systematically avoided the problem in the way i start up Gui4Cli)
Launching the Gui4Cli interpreter can be done in following ways
- 1. Gui4Cli is launched from a shell directly or with the Gui4Cli launcher "guis:gui" (not the OS4 prefs editor here) To avoid confusion i'll name it "gui4"
( gui4 takes the same options or tooltypes as Gui4Cli )
- 2. I launch Gui4Cli with a WBIcon and XICON as standard tool pointing to a DOS script
2.1 launching Gui4Clli directly
2.2 launching Gui4Cli using the "gui4" launcher
- 3. Gui4Cli is launched from Workbench icon with the Gui4Cli launcher "gui4" as standard tool.

I'll look into these cases more closely, but for now:
In the first case(s) the shell used for launching will be the output console unless you have defined an other console output with arg OUTPUT.
(i think you have such a shell in cases 2.1 & 2.2 too)

  1. // ------------------------ if CLI, READ COMMAND LINE ----------------
  2.  
  3. if (WBSTART==0)
  4. { rdargs =
  5. ReadArgs("FILE,D=DEBUG/S,B=BUFFERS/K/N,P=PORT/K,O=OUTPUT/K,E=EDITOR/K,N=NOREQUES
  6. /S", args, NULL);
  7. if (!args[0]) :::
  8. :::
  9. if (args[4])
  10. { strcpy (Defconsolespec, (char *)args[4]);
  11. DEFcp = ZERO; // * create console * / /OS4W BPTR was NULL
  12. }
  13. else DEFcp = Output(); >>>>>>>>>>>>>>>>>>
  14.  
  15. // *------------------- Get default console ---------------------* /
  16.  
  17. if (DEFcp == ZERO) // if no default console
  18. {
  19. if (DEFcp = Open (Defconsolespec, MODE_OLDFILE))
  20. { SelectOutput (DEFcp);
  21. DEFcpOPEN = 1;
  22. }
  23. else
  24. myerror (NULL, 1999, 0, "No console");
  25. }

When launched from WB however a predefined "Defconsolespec" will be used in the opeing of DEFcp
If the tooltuypes define an other one the latter will be used.

As a matter of fact in my Gui4Cli prefs (which is just another Gui4Cli script) ithe OUTPUT is set to KCON:something .
I do use case 2.1 as a standard launch.
A CON: window appears, that is then replaced with a KCON;
The only problem i had (systematically )was when closing Gui4Cli i had a message telling me some signalbit was not cleared.
Maybe i never verified what happens with the Con: shell when i us it.w
Time to do that.

JosDuchIt
JosDuchIt's picture
Offline
Last seen: 8 years 1 week ago
Joined: 2012-01-08 20:57
Like to thank everybodys who

Like to thank everybodys who has been helpfull answering this & other questions on this great forum
Resulting in my first major expansion of Gui4Cli's functionality:
http://www.os4coding.net/blog/josduchit/gui4cli-progress

Log in or register to post comments