![]() |
![]() |
#1 |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,393
|
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:
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; ... } 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()); ![]() Thanks in advance! ![]() |
![]() |
![]() |
#2 |
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 2,846
|
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. |
![]() |
![]() |
#3 | ||||
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,393
|
Hi Thomas and thank you so much for your answer!
Quote:
![]() Now I understand (sort of) why I got this "weird" behaviour! Quote:
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:
![]() Quote:
Best! Last edited by tygre; 30 November 2020 at 15:28. Reason: Typos; More info. |
||||
![]() |
![]() |
#4 | ||
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 2,846
|
Quote:
Quote:
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. 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. |
||
![]() |
![]() |
#5 | ||
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,393
|
Wow, very cool! Thanks Thomas!
![]() Quote:
![]() 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); ![]() Quote:
Cheers! |
||
![]() |
![]() |
#6 | |
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 2,846
|
Quote:
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. 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. |
|
![]() |
![]() |
#7 | ||
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,393
|
Quote:
![]() 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:
![]() Thanks again! |
||
![]() |
![]() |
#8 | |
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 2,846
|
Quote:
"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. |
|
![]() |
![]() |
#9 | |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,393
|
Quote:
![]() 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 20:27. Reason: More info.; Typos |
|
![]() |
![]() |
#10 |
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 2,846
|
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); |
![]() |
![]() |
#11 | |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,393
|
Hi Thomas and thank you very much
![]() Quote:
![]() Cheers! |
|
![]() |
![]() |
#12 |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,393
|
Hi Thomas and all!
So I took some time to try and I managed to get things working in "cooked" mode ![]() ![]() 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); What am I doing wrong? ![]() Cheers! |
![]() |
![]() |
#13 | |
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 2,846
|
Quote:
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(). |
|
![]() |
![]() |
#14 | ||||
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,393
|
Hi Thomas!
Quote:
Quote:
![]() Quote:
Quote:
Cheers! |
||||
![]() |
![]() |
#15 |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,393
|
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); } 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 16:38. |
![]() |
![]() |
#16 | |
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 2,846
|
Quote:
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. The loop looks fine as such, just drop the ReplyMsg(). |
|
![]() |
![]() |
#17 |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,393
|
Hi Thomas and thank you!
![]() Following your advice, I managed to get what I wanted ![]() 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); 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; } |
![]() |
![]() |
#18 |
Registered User
Join Date: Jan 2019
Location: Germany
Posts: 2,846
|
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. |
![]() |
![]() |
#19 |
botcher
Join Date: Jun 2016
Location: Hamburg/Germany
Posts: 674
|
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)); .. Code:
else if(input_event->ie_Class = IECLASS_CLOSEWINDOW) Code:
else if(input_event->ie_Class == IECLASS_CLOSEWINDOW) good job! |
![]() |
![]() |
#20 |
Returning fan!
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 1,393
|
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! |
![]() |
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
![]() |
||||
Thread | Thread Starter | Forum | Replies | Last Post |
NewsTek Episode 9 is now available for your listening pleasure. | Pyromania | Amiga scene | 4 | 26 June 2010 16:42 |
Listening to Amiga music does this | moriez | Nostalgia & memories | 4 | 02 April 2010 22:00 |
listening suggestions | Marcuz | request.Modules | 48 | 06 August 2008 11:02 |
NewsTek Episode 2 is now availabe for your listening pleasure. | Pyromania | Amiga scene | 0 | 21 April 2008 05:44 |
Amiga Round Table Podcast #17 is available for your listening pleasure! | Pyromania | Amiga scene | 1 | 18 December 2007 21:22 |
|
|