English Amiga Board


Go Back   English Amiga Board > Coders > Coders. Language > Coders. C/C++

 
 
Thread Tools
Old 29 November 2020, 23:01   #1
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 837
Getting the Current Console and Listening to It

Hi all!

I'm trying to write a CLI program that grabs the console and window of the current CLI/Shell and listens to key and mouse events from them. But I'm not getting the key/events that I was expecting and I'm running out of ideas...
(I read a lot on Google, including this post here on EAB.)

Here is what happens when I'm in "cooked" mode:
  1. My program is waiting for something to happen (with a CMD_READ, see below).
  2. I press any key, say "z", the corresponding char is printed in the console, but not by my program!
  3. Then, I can press another key, say "1", this key is not printed in the console.
  4. Then, I can press any other key, they will appear in the console.
  5. Finally, I must press RETURN for my program to get only the second pressed key, "1" from Step 3, from CMD_READ, nothing else...
Here is what happens when I'm in raw mode:
  1. My program is waiting for something to happen (with the same CMD_READ, see below).
  2. I press any key, say "1", the corresponding raw event is printed, but not by my program!
  3. Then, I can press any other key, they will appear in the console as chars, not raw events.
  4. Finally, I must press RETURN for my program to get only the first raw event, from Step 2, from CMD_READ, nothing else...
I was expecting to receive the key/raw event immediately when pressing a key, without it being printed in the console and without having to press RETURN.

Here is how I get the console and window (I removed all the error handling code):

Code:
    current_process = (struct Process *)FindTask(NULL);
    message_port = current_process->pr_ConsoleTask;
    ...
    if(!DoPkt(message_port, ACTION_DISK_INFO, ((ULONG) info_data) >> 2, 0L, 0L, 0L, 0L))
    ...
    if(!(_con_message_port = CreateMsgPort()))
    ...
    if(_con_IO_request = CreateIORequest(_con_message_port, sizeof(struct IOStdReq)))
    {
        _con_IO_request->io_Device = ((struct IOStdReq *)info_data->id_InUse)->io_Device;
        _con_IO_request->io_Unit   = ((struct IOStdReq *)info_data->id_InUse)->io_Unit;
        _con_window                =    (struct Window *)info_data->id_VolumeNode;
        ...
    }
and here is how I wait for the keys/events:

Code:
    _con_IO_request->io_Command = CMD_READ;
    _con_IO_request->io_Length  = 1 * sizeof(char);
    _con_IO_request->io_Data    = (APTR)&user_answer;
    SendIO((struct IORequest *)_con_IO_request);

    window_signal    = 1L << _con_window->UserPort->mp_SigBit;
    console_signal   = 1L << _con_message_port->mp_SigBit;
    received_signals = Wait(window_signal    |
                            console_signal   |
                            SIGBREAKF_CTRL_C |
                            controls_signal_playing_stopped());
What am I doing wrong? Why are keys/events "echoed" in the console? I must be missing something

Thanks in advance!
tygre is offline  
Old 30 November 2020, 13:18   #2
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 875
This cannot possibly work. The con-handler and your program are simultaneously issuing CMD_READs on the console.device, so it serves them on a "first-come-first-go" policy, confusing both. Second, you do not even know whether there is a console.device underneath the CON-handler. There isn't for AUX:, and there isn't for ViNCEd.



What you need to do is:


First switch to the RAW mode, then issue a Enable-Raw-Event CSI sequence (CSI-{ if I recall correctly) to the file handle the CON:Handler is working on, and then wait for raw-events. Either wait, or use WaitForChair() if your program should continue to loop. Once done, turn the events you need off again, switch back to "cooked".



Also, avoid ACTION_DISK_INFO. This may not do what you want (for example, if the console is not run by the console.device), and second,it may do more than you want (disable the AUTO-feature of a CON:-Window). It is bad practise. If you absolutely must (though not in this case) follow it by ACTION_UNDISK_INFO once done to release the handle to the window again.



Use the abstraction level the CON:-Handler provides: CSI-raw events, and WaitForChair(). This works also for AUX:-Handlers.
Thomas Richter is offline  
Old 30 November 2020, 16:02   #3
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 837
Hi Thomas and thank you so much for your answer!

Quote:
Originally Posted by Thomas Richter View Post
This cannot possibly work. The con-handler and your program are simultaneously issuing CMD_READs on the console.device, so it serves them on a "first-come-first-go" policy, confusing both. Second, you do not even know whether there is a console.device underneath the CON-handler. There isn't for AUX:, and there isn't for ViNCEd.
You can tell that I'm still a n00b
Now I understand (sort of) why I got this "weird" behaviour!

Quote:
Originally Posted by Thomas Richter View Post
First switch to the RAW mode, then issue a Enable-Raw-Event CSI sequence (CSI-{ if I recall correctly) to the file handle the CON:Handler is working on, and then wait for raw-events. Either wait, or use WaitForChar() if your program should continue to loop. Once done, turn the events you need off again, switch back to "cooked".
Thank you but could you help me further: how do I get this file handle? Would a Open("*", MODE_OLDFILE) be the right thing to do?

And,then, how can I use this file handle to get a message port and Wait(console_signal) with console_signal = _con_message_port->mp_SigBit?

PS. I read this FAQ answer but it uses ACTION_DISK_INFO so I'm kind of missing the link between a file handle to the console and the console message port/IO requests.

Quote:
Originally Posted by Thomas Richter View Post
Also, avoid ACTION_DISK_INFO. This may not do what you want (for example, if the console is not run by the console.device), and second,it may do more than you want (disable the AUTO-feature of a CON:-Window). It is bad practise. If you absolutely must (though not in this case) follow it by ACTION_UNDISK_INFO once done to release the handle to the window again.
Absolutely, thank you! I actually read your other post but I didn't know how to do any better

Quote:
Originally Posted by Thomas Richter View Post
Use the abstraction level the CON:-Handler provides: CSI-raw events, and WaitForChar(). This works also for AUX:-Handlers.
I think that I can't use WaitForChar() because I want also to wait for other signals, from the window and from another task of mine, or could I?

Best!

Last edited by tygre; 30 November 2020 at 16:28. Reason: Typos; More info.
tygre is offline  
Old 30 November 2020, 19:13   #4
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 875
Quote:
Originally Posted by tygre View Post
Thank you but could you help me further: how do I get this file handle? Would a Open("*", MODE_OLDFILE) be the right thing to do?
That opens a file whose handler is Process->pr_ConsoleTask. Typically the right thing to do, yes. Of course, it depends on what you want to do, i.e. how "redirection" should work for your program. If you simply use the result of Output(), then a user of your program could redirect this activity to another window via ">CON:...". If you use Input(), then "<CON:..." redirection does the job. And if you use "Open("*"...."), then "*>CON:.." redirects the console since 3.1.4.


Quote:
Originally Posted by tygre View Post
And,then, how can I use this file handle to get a message port and Wait(console_signal) with console_signal = _con_message_port->mp_SigBit?
Well, unless you want to do things "the tough way", you would use Read(), Write() and WaitForChair(), and the latter waits for a reaction. But if you want a message port, that can also be done, but it is more work. Create a port, and then use a struct StandardPacket (see dos/dosextens.h) to queue up read requests. This is a bit tricky, since you need to initialize it correctly.


stdpkt->sp_Msg.mn_Node.ln_Name needs to point to stdpkt->sp_Pkt (yes, really!), stdpkt->sp_Pkt.dp_Link needs to point to stdpk->sp_Msg, and stdpkt->sp_Pkt.dp_Port to your message port. Then set stdpkt->sp_Pkt.dp_Type to ACTION_READ. Next, you need to fiddle some data from the BPTR you received from Open(). Thus, first get a pointer to a "struct FileHandle" (see again dos/dosextens.h) from BADDR(fh), where fh is the result of the Open() call.



fh->fh_Arg1 needs to go into stdptk->sp_Pkt.dp_Arg1, stdpkt->sp_Pkt.dp_Arg2 is a pointer to the buffer to fill, and stdpkt->sp_Pkt.dp_Arg3 is the number of bytes to read.



PutMsg() then stdpkt->sp_Msg to the message port in fh->fh_Type (cast to struct MsgPort * if you have old includes).


You can now wait for a signal on the message port you created, or WaitPort() for this stdpkt to return. Re-establish all the linkages I mentioned to read more data.


Quote:
Originally Posted by tygre View Post
I think that I can't use WaitForChar() because I want also to wait for other signals, from the window and from another task of mine, or could I?
Well, you can set the timeout of WaitForChair() to 0 to get an immediate response. This is at least a lot simpler than the asynchronous I/O via dos packets I described above.
Thomas Richter is offline  
Old 30 November 2020, 20:08   #5
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 837
Wow, very cool! Thanks Thomas!

Quote:
Originally Posted by Thomas Richter View Post
That opens a file whose handler is Process->pr_ConsoleTask. Typically the right thing to do, yes. Of course, it depends on what you want to do, i.e. how "redirection" should work for your program. If you simply use the result of Output(), then a user of your program could redirect this activity to another window via ">CON:...". If you use Input(), then "<CON:..." redirection does the job. And if you use "Open("*"...."), then "*>CON:.." redirects the console since 3.1.4.

Well, unless you want to do things "the tough way", you would use Read(), Write() and WaitForChair(), and the latter waits for a reaction. But if you want a message port, that can also be done, but it is more work. Create a port, and then use a struct StandardPacket (see dos/dosextens.h) to queue up read requests. This is a bit tricky, since you need to initialize it correctly.

stdpkt->sp_Msg.mn_Node.ln_Name needs to point to stdpkt->sp_Pkt (yes, really!), stdpkt->sp_Pkt.dp_Link needs to point to stdpk->sp_Msg, and stdpkt->sp_Pkt.dp_Port to your message port. Then set stdpkt->sp_Pkt.dp_Type to ACTION_READ. Next, you need to fiddle some data from the BPTR you received from Open(). Thus, first get a pointer to a "struct FileHandle" (see again dos/dosextens.h) from BADDR(fh), where fh is the result of the Open() call.

fh->fh_Arg1 needs to go into stdptk->sp_Pkt.dp_Arg1, stdpkt->sp_Pkt.dp_Arg2 is a pointer to the buffer to fill, and stdpkt->sp_Pkt.dp_Arg3 is the number of bytes to read.

PutMsg() then stdpkt->sp_Msg to the message port in fh->fh_Type (cast to struct MsgPort * if you have old includes).

You can now wait for a signal on the message port you created, or WaitPort() for this stdpkt to return. Re-establish all the linkages I mentioned to read more data.
Interestingly, this code looks like a lot like that code here: I didn't realise (and would not have thought!) that I could replace ACTION_DISK_INFO with ACTION_READ to get data from the device

Code:
            sp->sp_Msg.mn_Node.ln_Name = (char *) &sp->sp_Pkt;
            sp->sp_Pkt.dp_Link         = &sp->sp_Msg;
            sp->sp_Pkt.dp_Port         = mp;
            sp->sp_Pkt.dp_Type         = ACTION_DISK_INFO;
            sp->sp_Pkt.dp_Arg1         = MKBADDR(id);

            PutMsg(cfh->fh_Type, &sp->sp_Msg);
            (void) WaitPort(mp);
            (void) GetMsg(mp);
I will try this week and let you know... probably with more question...

Quote:
Originally Posted by Thomas Richter View Post
Well, you can set the timeout of WaitForChair() to 0 to get an immediate response. This is at least a lot simpler than the asynchronous I/O via dos packets I described above.
Yes, but then it would be like a "busy wait"? I would need a loop listening (non-blocking) to my signal and non-blocking WaitForChar()'ing. Did I understand correctly?

Cheers!
tygre is offline  
Old 30 November 2020, 20:38   #6
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 875
Quote:
Originally Posted by tygre View Post
Interestingly, this code looks like a lot like that code here: I didn't realise (and would not have thought!) that I could replace ACTION_DISK_INFO with ACTION_READ to get data from the device
Well, yes, almost. There are two differences: dp_Arg1 needs to be initialized from fh_Arg1, and the code below is synchronous, and therefore overly complicated. In such a case, all the message/packet built-up is already present in the dos.library function DoPkt(), so it would definitely much less work. There is no good interface for asynchronous dos I/O.


IOWs, DoPkt() encapsules all the tough work in a single function call, but blocks until the handler returns - this is not what you asked for.


Quote:
Originally Posted by tygre View Post
Yes, but then it would be like a "busy wait"? I would need a loop listening (non-blocking) to my signal and non-blocking WaitForChar()'ing. Did I understand correctly?
Yes, with a timeout of 0, it would be a busy wait. With a timeoout, it would not be "quite as busy". It is admittedly not quite as flexible as asynchronous I/O, and has a longer delay, but it is also not quite as complicated.
Thomas Richter is offline  
Old 30 November 2020, 20:52   #7
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 837
Quote:
Originally Posted by Thomas Richter View Post
Well, yes, almost. There are two differences: dp_Arg1 needs to be initialized from fh_Arg1, and the code below is synchronous, and therefore overly complicated. In such a case, all the message/packet built-up is already present in the dos.library function DoPkt(), so it would definitely much less work. There is no good interface for asynchronous dos I/O.


IOWs, DoPkt() encapsules all the tough work in a single function call, but blocks until the handler returns - this is not what you asked for.
Sorry , I don't understand what you mean by "the code below is synchronous". Would the correct, asynchronous?, code look like this:

Code:
            sp->sp_Msg.mn_Node.ln_Name = (char *) &sp->sp_Pkt;
            sp->sp_Pkt.dp_Link         = &sp->sp_Msg;
            sp->sp_Pkt.dp_Port         = mp;
            sp->sp_Pkt.dp_Type         = ACTION_READ;
            sp->sp_Pkt.dp_Arg1         = MKBADDR(fh->fh_Arg1);
            sp->sp_Pkt.dp_Arg2         = &a_char;
            sp->sp_Pkt.dp_Arg3         = 1;

            PutMsg(cfh->fh_Type, &sp->sp_Msg);
            signal = Wait((1 << mp->mp_SigBit) | another_signal);
            GetMsg(mp);
            ...
            <Depending on the signal received, do something with the char>
            ...
Quote:
Originally Posted by Thomas Richter View Post
Yes, with a timeout of 0, it would be a busy wait. With a timeoout, it would not be "quite as busy". It is admittedly not quite as flexible as asynchronous I/O, and has a longer delay, but it is also not quite as complicated.


Thanks again!
tygre is offline  
Old 30 November 2020, 21:05   #8
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 875
Quote:
Originally Posted by tygre View Post
Sorry , I don't understand what you mean by "the code below is synchronous". Would the correct, asynchronous?

"Synchronous" means that it is "synchronized" to the handler, i.e. it waits until the handler returns. "Asynchronous" means that the handler and your code are not synchronized, and execute in parallel. This is what you want.


BTW, in the code you posted, Wait(1<<signal) is not quite what you want since the signal could be set from a call before. In such a case, a WaitPort() would be better. Anyhow, it is synchronous (it blocks), so not what you need.
Thomas Richter is offline  
Old 30 November 2020, 21:22   #9
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 837
Quote:
Originally Posted by Thomas Richter View Post
"Synchronous" means that it is "synchronized" to the handler, i.e. it waits until the handler returns. "Asynchronous" means that the handler and your code are not synchronized, and execute in parallel. This is what you want.


BTW, in the code you posted, Wait(1<<signal) is not quite what you want since the signal could be set from a call before. In such a case, a WaitPort() would be better. Anyhow, it is synchronous (it blocks), so not what you need.
Ah, yes, indeed!

Then, how could I be "listening" to both the Console (not through its message port?) and other signals, like SIGBREAKF_CTRL_C, a signal from my other task, and a signal from the window?

I thought that Wait((1 << mp->mp_SigBit) | ...) would do the trick... Maybe with SetSignal(0, mp->mp_SigBit) first?

Thanks

Last edited by tygre; 30 November 2020 at 21:27. Reason: More info.; Typos
tygre is offline  
Old 01 December 2020, 08:13   #10
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 875
You typically try to GetMsg() from all ports to process data that came in, and only if none of the ports provided a message, you run into the Wait(). The SetSignal() upfront is also not doing the right thing as it leaves a race condition where the signal is triggered right upfront SetSignal(), and then you deadlock.

The general message processing loop is something like:
Code:
do {
if (msg = GetMsg(port1)) {
  ProcessMsg1(msg);
} else if (msg = GetMsg(port2)) {
  ProcessMsg2(msg);
} else .... {

} else Wait(allsignals);
}while(!aborted);
Thus, "Wait" is the "last resort" if nothing else happened. This loop is safe because even if a signal is set in the Wait() from a last message, the code just loops again and polls messages again, which is harmless. In the second round, it will then block and wait for an event to happen.
Thomas Richter is offline  
Old 01 December 2020, 16:01   #11
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 837
Hi Thomas and thank you very much

Quote:
Originally Posted by Thomas Richter View Post
You typically try to GetMsg() from all ports to process data that came in, and only if none of the ports provided a message, you run into the Wait(). The SetSignal() upfront is also not doing the right thing as it leaves a race condition where the signal is triggered right upfront SetSignal(), and then you deadlock.

The general message processing loop is something like:
Code:
do {
if (msg = GetMsg(port1)) {
  ProcessMsg1(msg);
} else if (msg = GetMsg(port2)) {
  ProcessMsg2(msg);
} else .... {

} else Wait(allsignals);
}while(!aborted);
Thus, "Wait" is the "last resort" if nothing else happened. This loop is safe because even if a signal is set in the Wait() from a last message, the code just loops again and polls messages again, which is harmless. In the second round, it will then block and wait for an event to happen.
This helps a lot, and thanks for the explanation about the race condition... I will try all this during the week and let you know

Cheers!
tygre is offline  
Old 03 December 2020, 05:59   #12
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 837
Hi Thomas and all!

So I took some time to try and I managed to get things working in "cooked" mode but not in "raw" mode... For some reasons, in raw mode, I receive the raw input event in pieces and with missing pieces . Here is my (simplified) code:

Code:
    if((_con_file = Open("*", MODE_OLDFILE)) == 0) { ... }
    SetMode(_con_file, 1);
    printf("\33[1;11{");

    if((_con_file_handle = BADDR(_con_file))->fh_Type == NULL) { ... }

    _con_message_port = (struct MsgPort *)_con_file_handle->fh_Type;

    if((_con_reply_port = CreatePort(NULL, 0)) == NULL) { ... }

    if((_con_standard_packet = AllocMem(sizeof (struct StandardPacket), MEMF_PUBLIC | MEMF_CLEAR)) == NULL) { ... }

    _con_standard_packet->sp_Msg.mn_Node.ln_Name = (char *)&_con_standard_packet->sp_Pkt;
    _con_standard_packet->sp_Pkt.dp_Link         = &_con_standard_packet->sp_Msg;
    _con_standard_packet->sp_Pkt.dp_Port         = _con_reply_port;
    _con_standard_packet->sp_Pkt.dp_Type         = ACTION_READ;
    _con_standard_packet->sp_Pkt.dp_Arg1         = MKBADDR(_con_file_handle->fh_Arg1);
    _con_standard_packet->sp_Pkt.dp_Arg2         = (LONG)&buffer;
    _con_standard_packet->sp_Pkt.dp_Arg3         = (LONG)BUFFER_LENGTH;

    PutMsg(_con_message_port, &_con_standard_packet->sp_Msg);

    do
    {
        if(message = GetMsg(_con_reply_port))
        {
            ReplyMsg(message);
            printf("THERE1: \"%s\"\n", answer);
        }
        else
        {
            console_signal   = 1L << _con_reply_port->mp_SigBit;
            received_signals = Wait(console_signal   |
                                    SIGBREAKF_CTRL_C);
            printf("THERE2: \"%s\"\n", answer);
            break;
        }
    }
    while(TRUE);
No matter the size of the buffer, I will see pieces of the raw input event after THERE1 and THERE2, some garbage too, but not the whole event. I'm missing the class, subclass, and keycode parts but (I think that) I have maybe the secs and microsecs...

What am I doing wrong?

Cheers!
tygre is offline  
Old 03 December 2020, 09:54   #13
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 875
Quote:
Originally Posted by tygre View Post
Hi Thomas and all!

So I took some time to try and I managed to get things working in "cooked" mode but not in "raw" mode... For some reasons, in raw mode, I receive the raw input event in pieces and with missing pieces . Here is my (simplified) code:
First of all, arg1 is really a plain copy from the filehandle structure, no conversion to a BPTR required. It is just a "handle" the handler left in the file handle to identify the file.


Actually, this should *only* work in raw mode, but never in cooked mode. The CON: handler is not able to foreward or identify raw events in cooked mode (ViNCEd does, but CON: does not).


Dos packets are never "replied". If you receive the answer, the packet is done. If you want to send it again, you need to re-establish the linkage via msg->mp_Node.ln_Name and stdpkt->dp_Link (re-install the pointers).


The number of characters that were received are found in stdpkt->sp_Pkt.dp_Res1 (or -1 for an error), the secondary result code (stdpkt->sp_Pkt.dp_Res2) contains the error code the dos.library would usually place in IoErr().
Thomas Richter is offline  
Old 04 December 2020, 15:25   #14
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 837
Hi Thomas!

Quote:
Originally Posted by Thomas Richter View Post
First of all, arg1 is really a plain copy from the filehandle structure, no conversion to a BPTR required. It is just a "handle" the handler left in the file handle to identify the file.
Okay, I will change that in my code!

Quote:
Originally Posted by Thomas Richter View Post
Actually, this should *only* work in raw mode, but never in cooked mode. The CON: handler is not able to foreward or identify raw events in cooked mode (ViNCEd does, but CON: does not).
Oh, then I don't know why it works as it does right now , maybe because I'm using KingCON?

Quote:
Originally Posted by Thomas Richter View Post
Dos packets are never "replied". If you receive the answer, the packet is done. If you want to send it again, you need to re-establish the linkage via msg->mp_Node.ln_Name and stdpkt->dp_Link (re-install the pointers).
Yes, it's what I actually do: I "loop" starting from the if((_con_standard_packet = AllocMem(sizeof (struct StandardPacket), MEMF_PUBLIC | MEMF_CLEAR)) == NULL) { ... }, creating a new packet and binding it and sending it every time...

Quote:
Originally Posted by Thomas Richter View Post
The number of characters that were received are found in stdpkt->sp_Pkt.dp_Res1 (or -1 for an error), the secondary result code (stdpkt->sp_Pkt.dp_Res2) contains the error code the dos.library would usually place in IoErr().
Okay, thanks! Will try that too and let you know!

Cheers!
tygre is offline  
Old 04 December 2020, 16:38   #15
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 837
So, I tried the following "loop", printing out the number of chars received:

Code:
    ... <as above in a previous post with SetMode(_con_file, 1);> ...

    while(TRUE)
    {
        if(!(_con_standard_packet = AllocMem(sizeof (struct StandardPacket), MEMF_PUBLIC | MEMF_CLEAR)) == NULL) { ... }

        _con_standard_packet->sp_Msg.mn_Node.ln_Name = (char *)&_con_standard_packet->sp_Pkt;
        _con_standard_packet->sp_Pkt.dp_Link         = &_con_standard_packet->sp_Msg;
        _con_standard_packet->sp_Pkt.dp_Port         = _con_reply_port;
        _con_standard_packet->sp_Pkt.dp_Type         = ACTION_READ;
        _con_standard_packet->sp_Pkt.dp_Arg1         = _con_file_handle->fh_Arg1;
        _con_standard_packet->sp_Pkt.dp_Arg2         = (LONG)&buffer;
        _con_standard_packet->sp_Pkt.dp_Arg3         = (LONG)BUFFER_LENGTH;

        PutMsg(_con_message_port, &_con_standard_packet->sp_Msg);

        do
        {
            if(message = GetMsg(_con_reply_port))
            {
                printf("THERE1:   %d  \n", _con_standard_packet->sp_Pkt.dp_Res1);
                printf("THERE1: \"%s\"\n", answer);
                ReplyMsg(message);
            }
            else
            {
                console_signal   = 1L << _con_reply_port->mp_SigBit;
                received_signals = Wait(console_signal   |
                                                    SIGBREAKF_CTRL_C);
                printf("THERE2:   %d  \n", _con_standard_packet->sp_Pkt.dp_Res1);
                printf("THERE2: \"%s\"\n", answer);
               break;
           }
       }
       while(TRUE);
   }
and still I don't get the whole raw input event. The value of dp_Res1 varies from 0 to BUFFER_LENGTH but the content of answer is mostly gibberish or parts of the input event, never the "whole" event...

I must be doing something wrong! Am I talking/listening to the right ports? I think that my bindings are correct but should I "reset"/rebind something in the loop?

Best!

Last edited by tygre; 05 December 2020 at 17:38.
tygre is offline  
Old 05 December 2020, 19:41   #16
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 875
Quote:
Originally Posted by tygre View Post
So, I tried the following "loop", printing out the number of chars received:
Please do not "Reply" to the message. Once you received the message back, it is done. It is the console handler which replies it - but by different means.


The StdPkt does not require to be allocated, you can also leave it on the stack in this particular case. Other than that, this should work fine in Raw mode. Note that the buffer is not necessarily filled completey. Thus, how did you find that there is nonsense in the buffer? Actually, as you only receive dp_Res1 bytes back, only the bytes 0 to dp_Res1 in buffer are valid. Also note that the raw event is a CSI sequence, so if you attempt to print it out, the console will (again) attempt to interpret the sequence. I thus suggest that you print out the hex codes from bytes 0 to dp_Res-1 instead of the character literals.

Quote:
Originally Posted by tygre View Post
I must be doing something wrong! Am I talking/listening to the right ports? I think that my bindings are correct but should I "reset"/rebind something in the loop?
The loop looks fine as such, just drop the ReplyMsg().
Thomas Richter is offline  
Old 08 December 2020, 23:08   #17
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 837
Hi Thomas and thank you!

Following your advice, I managed to get what I wanted I was doing several things things wrong, like setting up raw mode but not telling the Console which raw input events I wanted... I was also printing the CSI char at the beginning of the events, which was "messing" the output...

If others are interested, here is the gist of the solution, the whole code is available in the _get_char() function:

Code:
	if((_con_file = Open("*", MODE_OLDFILE)) == 0) { ...	}
	// Enable RAW mode
	SetMode(_con_file, 1);
	// Listen to keyboard and close gadget
	printf("\33[1;11{");
	if((_con_file_handle = BADDR(_con_file))->fh_Type == NULL) { ... }
	_con_message_port = (struct MsgPort *)_con_file_handle->fh_Type;
	if((_con_reply_port = CreatePort(NULL, 0)) == NULL) { ... }

	while(TRUE)
	{
		con_standard_packet                         = AllocMem(sizeof(struct StandardPacket), MEMF_PUBLIC | MEMF_CLEAR);
		con_standard_packet->sp_Msg.mn_Node.ln_Name = (char *)&con_standard_packet->sp_Pkt;
		con_standard_packet->sp_Pkt.dp_Link         = &con_standard_packet->sp_Msg;
		con_standard_packet->sp_Pkt.dp_Port         = _con_reply_port;
		con_standard_packet->sp_Pkt.dp_Type         = ACTION_READ;
		con_standard_packet->sp_Pkt.dp_Arg1         = _con_file_handle->fh_Arg1;
		con_standard_packet->sp_Pkt.dp_Arg2         = (LONG)&_con_answer;
		con_standard_packet->sp_Pkt.dp_Arg3         = (LONG)ANSWER_LENGTH;

		PutMsg(_con_message_port, &con_standard_packet->sp_Msg);

		do
		{
			if(GetMsg(_con_reply_port))
			{
				// Nothing to do
			}
			else
			{
				console_signal   = 1L << _con_reply_port->mp_SigBit;
				received_signals = Wait(console_signal | SIGBREAKF_CTRL_C);
				break;
			}
		}
		while(TRUE);

		received_char = '\0';
		if(received_signals & console_signal &&
		   _convert_CSI_to_InputEvent((char *)&_con_answer[1], &input_event) == RETURN_OK)
		{
			if(input_event->ie_Class == IECLASS_RAWKEY)
			{
				received_char = _convert_InputEvent_to_ANSI(input_event);
			}
			else if(input_event->ie_Class = IECLASS_CLOSEWINDOW)
			{
				received_char = ...;
			}
			free(input_event);
		}
		else if(received_signals & SIGBREAKF_CTRL_C)
		{
			received_char = ...;
		}

		// We stop listening only when we received an interesting char
		if(isprint(received_char) ||
		   received_char == '\n'  ||
		   received_char == '\r')
		{
			break;
		}
	}

	printf("%c", received_char);
	fflush(stdout);
I use my own function to convert the CSI sequence into an InputEvent and use the keymap.library function MapRawKey() to convert a key raw input event into the corresponding char, according to the user's keyboard layout.

Code:
static int _convert_CSI_to_InputEvent(
	IN  char               *csi,
	OUT struct InputEvent **input_event)
{
	char *string = NULL;
	char *token  = NULL;

	string = string_duplicate(csi);
	*input_event = malloc(sizeof(struct InputEvent));

	// ie_NextEvent
	(*input_event)->ie_NextEvent = NULL;

	// ie_Class
	token = strtok(string, ";");
	if(token == NULL)
	{
		goto _RETURN_ERROR;
	}
	else if(string_equal(token, "1"))
	{
		(*input_event)->ie_Class = IECLASS_RAWKEY;
	}
	else if(string_equal(token, "11"))
	{
		(*input_event)->ie_Class = IECLASS_CLOSEWINDOW;
		goto _RETURN_OK;
	}
	else
	{
		// Let's pretend it's nothing
		(*input_event)->ie_Class = IECLASS_NULL;
		goto _RETURN_OK;
	}

	// ie_SubClass
	token = strtok(NULL, ";");
	if(token == NULL)
	{
		goto _RETURN_ERROR;
	}
	(*input_event)->ie_SubClass = IESUBCLASS_COMPATIBLE;

	// ie_Code
	token = strtok(NULL, ";");
	if(token == NULL)
	{
		goto _RETURN_ERROR;
	}
	(*input_event)->ie_Code = atoi(token);

	// ie_Qualifier
	token = strtok(NULL, ";");
	if(token == NULL)
	{
		goto _RETURN_ERROR;
	}
	(*input_event)->ie_Qualifier = atoi(token);

	// ie_position
	token = strtok(NULL, ";");
	if(token == NULL)
	{
		goto _RETURN_ERROR;
	}
	(*input_event)->ie_position.ie_xy.ie_x = atoi(token);
	token = strtok(NULL, ";");
	if(token == NULL)
	{
		goto _RETURN_ERROR;
	}
	(*input_event)->ie_position.ie_xy.ie_y = atoi(token);

	// ie_TimeStamp
	token = strtok(NULL, ";");
	if(token == NULL)
	{
		goto _RETURN_ERROR;
	}
	(*input_event)->ie_TimeStamp.tv_secs = atoi(token);
	token = strtok(NULL, "|");
	if(token == NULL)
	{
		goto _RETURN_ERROR;
	}
	(*input_event)->ie_TimeStamp.tv_micro = atoi(token);

	goto _RETURN_OK;
	_RETURN_OK:
		free(string);
		return RETURN_OK;

	goto _RETURN_ERROR;
	_RETURN_ERROR:
		if(*input_event != NULL)
		{
			free(*input_event);
			*input_event = NULL;
		}
		if(string != NULL)
		{
			free(string);
		}
		return RETURN_ERROR;
}

static char _convert_InputEvent_to_ANSI(
	IN struct InputEvent *input_event)
{
	char buffer[2] = { 0 };
	char c         = '\0';

	if(MapRawKey(input_event, buffer, 2, NULL))
	{
		c = buffer[0];
	}
	
	return c;
}
Cheers!
tygre is offline  
Old 09 December 2020, 11:29   #18
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 875
Quote:
Originally Posted by tygre View Post
Hi Thomas and thank you!

Some small comments: You want to release the StdPacket at the end of your program, of course, also please turn off the raw events you don't need as otherwise you may confuse the CON: handler. The window close event is already active (unless you opened a RAW: window), so no need to set or reset it. Also, switch back to the cooked mode once done.
Thomas Richter is offline  
Old 09 December 2020, 16:54   #19
bebbo
botcher

 
Join Date: Jun 2016
Location: Hamburg/Germany
Posts: 493
you might enhance your code and do less alloc/free by using stack vars like
Code:
	struct StandardPacket con_standard_packet;
	char                   received_char         = '\0';
	ULONG                  console_signal        = 0L;
	ULONG                  received_signals      = 0L;
	struct InputEvent     input_event;

	while(TRUE)
	{
		memset(&con_standard_packet, 0, sizeof(struct StandardPacket));

..
this
Code:
			else if(input_event->ie_Class = IECLASS_CLOSEWINDOW)
should be fixed to
Code:
			else if(input_event->ie_Class == IECLASS_CLOSEWINDOW)
plus I don't see a reason for the string duplication...

good job!
bebbo is offline  
Old 09 December 2020, 22:40   #20
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 837
Hi Thomas and Bebbo!

Thanks for your advice

Actually, I did try to allocate the StandardPacket on the stack (and reset it using memset) but the loop would "freeze" after the receiving the first raw input event

Same, if I add a free(con_standard_packet), then Fortify complains that I'm freeing the allocated memory twice

So I went for allocating a StandardPacket every iteration without explicitly freeing it... Could it be because this StandardPacket is "consumed" somehow by the Console?

Cheers!
tygre is offline  
 


Currently Active Users Viewing This Thread: 3 (0 members and 3 guests)
 
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
NewsTek Episode 9 is now available for your listening pleasure. Pyromania Amiga scene 4 26 June 2010 17:42
Listening to Amiga music does this moriez Nostalgia & memories 4 02 April 2010 23:00
listening suggestions Marcuz request.Modules 48 06 August 2008 12:02
NewsTek Episode 2 is now availabe for your listening pleasure. Pyromania Amiga scene 0 21 April 2008 06:44
Amiga Round Table Podcast #17 is available for your listening pleasure! Pyromania Amiga scene 1 18 December 2007 22:22

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 14:47.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2021, vBulletin Solutions Inc.
Page generated in 0.13162 seconds with 15 queries