Procedural programs in an event-based GUI environment

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
AndrewAPrice
Member
Member
Posts: 2306
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Procedural programs in an event-based GUI environment

Post by AndrewAPrice »

I'm trying to work out how to handle procedural programs in a GUI environment.

Applications in my OS are event based. That is, they're based around a messaging loop:

Code: Select all

// main loop
while(running)
{
   GetNextMessage();

   // handle message (click, key down, redraw window, etc)
}
But this design doesn't suit procedural (usually console) based programs like:

Code: Select all

int main()
{
   char name[100];
   printf("What is your name? ");
   scanf("%s", name);
   return 0;
}
Yet most of the command line utilities I will port to my OS will be written like that.

I'm thinking when reading/writing to stdout/stdin/stderr for the first time I should create a console window (that way GUI applications which don't write to/read from the console won't have standard console window open) inside the newlib backend.

But then, there is also the problem of handling window events (redraw, move, get input, closed) and file IO messages. Especially things like file IO, require a lot of messaging due to my microkernel design.

So I was thinking, in of the newlib backend inside open/read/lseek/close/reed etc I should have an event loop that flush out the remaining messages (window and input, etc), hoping that at least one of these functions are called often enough that the program doesn't become unresponsive (e.g. you close the window but the program is too busy processing that it doesn't call one of these functions to handle the event).

But if this loop handles handles events and messages, ones it ignores (not targetted at the console window, and non-io ones) are will be deleted if the messenger hasn't be initialised that way they won't pile up and flood an innocent procedural program. (An event based program will initialise the messenger and handle it themselves).

What I really want to know, is how do other microkernels handle this?
My OS is Perception.
clange
Member
Member
Posts: 163
Joined: Sun Oct 05, 2008 5:00 am
Location: Copenhagen, Denmark
Contact:

Re: Procedural programs in an event-based GUI environment

Post by clange »

Hi

I have been thinking about how to handle console applications in my gui (not yet written - only thinking about it).

I decided to simply "wrap" console applications. Since console applications will use stdin, stdout and stderr it should be pretty simple to setup the correct pipes when the console application is loaded. Then it is simply a matter of having a gui based terminal application which handles mouse events etc. by it self and translates keyboard events into writes to a pipe. Data received from the pipes connected to the console application should similarly be translated into gui actions that displays the output in a window.

This is of course the most basic approach - I would like a tabbed terminal application which supports VT100 emulation etc. :D

This solution is based on standard funtionality (pipes) and does not require any "hacking". It does however require a quite that the rest of your os is quite mature (but so does a gui).

Hope this will inspire you.

clange

BTW my os is also micro kernel based.
User avatar
AndrewAPrice
Member
Member
Posts: 2306
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: Procedural programs in an event-based GUI environment

Post by AndrewAPrice »

clange wrote:Hope this will inspire you.
It did! I could write a reasonably featured terminal emulator independent of the program launched in it.

How would you handle programs that don't have their stdout/stdin/stderr mapped (e.g. it wasn't launched inside of a terminal emulator)? There is still the problem of handling messages. The problem I can think of off the top of my head is opening files - like when waiting for a message back from the VFS that you have permission to access the file and that it exists and to open a pipe. Then also other messages like handling if the VFS for some reason closes the pipe that you can't ignore.
My OS is Perception.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Procedural programs in an event-based GUI environment

Post by Brendan »

Hi,
MessiahAndrw wrote:I'm trying to work out how to handle procedural programs in a GUI environment.
You need a "CheckForMessage()" function that's called regularly, and whenever a message is received (either from "CheckForMessage()" or from "GetMessage()") you'd call a "HandleMessage()" function.

The "HandleMessage()" function needs to buffer anything that was received. For example, if a message arrives that contains a byte from STDIN you'd put the byte on a FIFO buffer.

Then you'd have a function to get a byte from STDIN which checks if there's any bytes in the FIFO buffer already, and if there isn't it waits for one (while calling "GetMessage()" and "HandleMessage()"). Also, you'd put calls to "CheckForMessage()" everywhere, to make sure received messages get handled (even if the received message are for something completely different). For example:

Code: Select all

int getByteFromSTDIN(void) {
    if(FIFO is empty) {
        do {
            message = GetMessage();
            HandleMessage(message);
        } while(FIFO is empty);
    } else {
        /* This is only here to handle unrelated messages */
        message = CheckForMessage();
        if(message != NULL) HandleMessage(message);
    }
    c = getNextByteFromFIFO();
}
The same buffering needs to happen for almost everything, because you can't discard messages. The only alternative here is to signals - for example, if your OS sends a "TERMINATE" message to the application, then your message handler might pretend it's a signal and call a signal handler.

If you think about it enough, you'll realize that you can hide all of the messaging in a library, and even implement a standard C/POSIX library for legacy/procedural programs.

However, the beautiful thing about event driven programming is that you wait for any event and handle whatever arrives, so if you're expecting the user to press a key and they move the mouse instead (or if you receive data from the network or anything else) it doesn't matter. The problem with procedural programs is they can't handle this easily (e.g. if you're waiting for the file system to "open()" a file then everything else has to wait until the file system has opened the file) which can cause less efficiency and less responsive software (or, software that needs to use many threads to avoid these problems). Even when the entire OS is event driven, applications that hide everything under a library and using procedural code will give you the same problems as you'd get if the entire OS was procedural to begin with. Therefore (IMHO) the best approach is to write event driven code (and rewrite software from other OSs instead of porting it), instead of using a standard C/POSIX library for legacy/procedural programs that hides the event driven nature of the OS.

Of course there's always a compromise between programmer time and software quality, and sometimes crappy code that works is better than good code that isn't completed yet. This is a matter of opinion though - IMHO good code that isn't completed yet eventually becomes good code that is completed, but crappy code that works tends to stay crappy forever because there's less incentive to replace it with good code (it's always "good enough for now").


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
AndrewAPrice
Member
Member
Posts: 2306
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: Procedural programs in an event-based GUI environment

Post by AndrewAPrice »

Brendan wrote:Therefore (IMHO) the best approach is to write event driven code (and rewrite software from other OSs instead of porting it), instead of using a standard C/POSIX library for legacy/procedural programs that hides the event driven nature of the OS.
I completely agree with you here.

Sometimes that isn't feasibly (e.g. imagine rewriting GCC to be event driven).

I'm also expecting procedural and event code to be mixed.

For example, even if I had the most beautifully-crafted event-based IO library, I'm sure plenty of developers (including me) would revert to using fread in an event-driven application for loading/saving when they don't require asynchronous access. The same would apply if you were writing a command line utility to use scanf.

So as much as I would try to avoid and discourage procedural code in my OS, I can't ignore it.

Thankfully, most GUI applications and libraries are event-driven (hopefully meaning easier to port) but even then I'm sure a lot still rely on fopen/fread for file IO.

So the problem is, I can emulate a POSIX/C-lib system on top to make it easy to port code, but I don't want it to take complete control over the event managing, so I need to find a system that works with both (I have plenty of ideas in my head how to now, but I must go).
My OS is Perception.
clange
Member
Member
Posts: 163
Joined: Sun Oct 05, 2008 5:00 am
Location: Copenhagen, Denmark
Contact:

Re: Procedural programs in an event-based GUI environment

Post by clange »

MessiahAndrw wrote: How would you handle programs that don't have their stdout/stdin/stderr mapped (e.g. it wasn't launched inside of a terminal emulator)?
By differentiating between "legacy" appliactions and "modern" applications. Modern applications are the ones that knows about the target system (all your own applications :D ) and the legacy being all the standard terminal based programs (your own or ported). Modern applications register with the system to intact with the GUI, uses your nice asynchronous I/O API, all the nice stuff etc. Legacy applications will be started by a shell (any shell) started inside a terminal emulator. This way I can ensure that standard pipes are setup correctly.
MessiahAndrw wrote: There is still the problem of handling messages. The problem I can think of off the top of my head is opening files - like when waiting for a message back from the VFS that you have permission to access the file and that it exists and to open a pipe.
The legacy application should never even know in which environment it is run. It simply links to libc which provides functions to open files, pipes, etc. These of course uses messages to call the rest of my system but this should give me transparent integration with the rest of my system. When fopen() is called libc sends a message to the VFS and the legacy application process (ordinary process) will be blocked until the request can be completed. Writing to stdin will result in a message to the VFS which will relay it to the terminal emulator.
MessiahAndrw wrote:Then also other messages like handling if the VFS for some reason closes the pipe that you can't ignore.
I haven't thought about all the details yet, but if stdin, stdout or stderr suddenly closes I would imagine that any application would be pretty screwed up anyway. And it shouldn't happen unless the terminal emulator crashes (and if this happens it is an error that can be detected and the pipes could be adopted by a system process or similiar). Could someone enlighten me on what the standards says with reagrds to this?

But the entire scheme requires some cooperation from the VFS and the console and keyboard (or terminal) drivers. But I prefer to hide the complexity here rather than elsewhere. And it doesn't require that much code to support it I think.
Brendan wrote: Even when the entire OS is event driven, applications that hide everything under a library and using procedural code will give you the same problems as you'd get if the entire OS was procedural to begin with.
Wouldn't the problems be confined to the single process containing the procedural code?
Brendan wrote:Therefore (IMHO) the best approach is to write event driven code (and rewrite software from other OSs instead of porting it), instead of using a standard C/POSIX library for legacy/procedural programs that hides the event driven nature of the OS.
Agree. My main idea is to gain access to LOTS of ready made applications with little as little investment as possible. That is why I decided to implement libc and POSIX. But so far I have ported exactly zero applications - I prefer to write my own code since this is only a hobby (later I envisions that I have to port code to achieve real progress - font rendering for example).

clange
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Procedural programs in an event-based GUI environment

Post by Brendan »

Hi,
MessiahAndrw wrote:So the problem is, I can emulate a POSIX/C-lib system on top to make it easy to port code, but I don't want it to take complete control over the event managing, so I need to find a system that works with both (I have plenty of ideas in my head how to now, but I must go).
In this case you'd need to start with a POSIX/C-lib system (with all messaging/events hidden), and then let application disable the handling for certain sub-systems. For example, let the application tell the library not to handle any messages from STDIN and to forward these messages to it's own handler.

This could actually create a huge re-entrancy mess though, not unlike signal handling in traditional procedural environments. For e.g. a thread calls any procedural library function, which could call the thread's own message handler, and the thread's message handler needs to be capable of running when the thread is in the middle of any procedural library function (which could have been called when the thread was in the middle of any critical section).

To be honest, I'd assume that often attempting to handle a mixture of event driven and procedural would be more complex than only using event driven or only using procedural.

I'd also point out that I've never found a situation where a process couldn't be implemented as purely event driven; but I've seen plenty of situations where procedural programming is used poorly (e.g. where applications refuse to respond to the user because they're waiting for I/O). Because of this I've always toyed with the idea of making it impossible for any procedural code to exist on my OS - force everything to use messaging and event driven code, and refuse to allow legacy/procedural stuff to be ported without major changes.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Procedural programs in an event-based GUI environment

Post by Combuster »

Some practicalities:

Have the binaries differ based on whether they are stdio-oriented or GUI-oriented. You could link to a different runtime, or (stolen from windows) have a bit in the header that tells the application loader to start a shell first.

As for the actual processing, create two threads (one additional thread started by the runtime) - one is continuously processing events, while the other is the "actual program". The necessary information can then be communicated between the two threads without losing responsiveness from the events thread (which is what you wanted, right?).
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Post Reply