Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!shadooby!samsung!zaphod.mps.ohio-state.edu!usc!snorkelwacker!bloom-beacon!eru!luth!sunic!tut!hydra!hylka!kulokari From: kulokari@cc.helsinki.fi Newsgroups: comp.os.os2 Subject: Keyboard, mouse and threads (Was: Re: uEMACS) Message-ID: <1688.25a364a4@cc.helsinki.fi> Date: 4 Jan 90 14:58:44 GMT References: <5345@udccvax1.acs.udel.EDU> <976@tuminfo1.lan.informatik.tu-muenchen.dbp.de> <1654.2590d3f4@cc.helsinki.fi> <1020@tuminfo1.lan.informatik.tu-muenchen.dbp.de> Organization: University of Helsinki Lines: 229 In article <1020@tuminfo1.lan.informatik.tu-muenchen.dbp.de>, rommel@lan.informatik.tu-muenchen.dbp.de (Kai-Uwe Rommel) writes: > I know about the busy loop problem. But I already tried to use to > separate threads for keyboard and mouse input. I was surprised to see > that the MouReadEventQueue() call apears to use busy wait too when the > MOUSE_WAIT mode is used. When my mouse thread waited for an event, my > other thread waiting for keyboard input was blocked !?! Any hints ? > Currently I use the MOUSE_NOWAIT mode and make some DosSleep() calls > between the MouReadEventQueue() calls. > Mouse and keyboard handling and the use of threads therein, with special reference to uEmacs, does seem to interest many of us. I have had no problems in getting the system to work. Stopping it is a lot harder, as will be demonstrated below. This message is a bit lengthy, sorry. The problem is, how to use OS/2 threads to read keyboard and mouse input and merge them into one message queue (fifo), if possible without busy loop polling. This much for the background. The basic solution, written in somewhat stylized and abridged C, is the following: -------------- int stop, threads; unsigned long something; kbd_thread() { threads++; while (!stop) { /* read a key, translate to character string and put it in FIFO */ ... /* indicate there is something in FIFO */ DosSemClear(&something); } threads--; DosExit(0,0); } mou_thread() { /* analogous to kbd_thread */ } get_char() { if (somethinginfifo()) return (getfromfifo()); DosSemSetWait(&something,-1L); /* wait until there is something again... */ return (getfromfifo()); } main() { char c; ... stop = 0; threads=0; /* start threads */ ... /* main loop */ while (!stop) c = get_char(); ... /* something */ ... } /* wait for threads to kill themselves */ while (threads); ... } ----------------- This is simple and beautiful, and works well until you try to exit. Because both threads are always waiting for the "next" keyin/mouse event, you must both press a key and click the mouse before the threads get the change to notice that they are expected to die. Of course, if you just brutally exit the program, the threads will be gone too, but in many cases this is not what you want. For example, you must be able to deactivate keyboard and mouse handlers when you spawn subprocesses, and restore them afterwards. So we must eliminate the read-ahead. Let us introduce another semaphore, 'proceed', and modify the code: --------------- int stop, threads; unsigned long something, proceed; kbd_thread() { threads++; while (1) { DosSemWait(&proceed,-1L); /* wait for permission to continue */ if (stop) { threads--; DosExit(0,0); } /* read a key, translate to character string and put it in FIFO */ ... /* indicate there is something in FIFO */ DosSemClear(&something); DosSemSet(&proceed); } } mou_thread() { /* analogous to kbd_thread */ } get_char() { if (somethinginfifo()) return (getfromfifo()); DosSemSet(&something); /* indicate fifo is empty */ DosSemClear(&proceed); /* allow threads to continue */ DosSemWait(&something,-1L); /* wait until there is something again ... */ return (getfromfifo()); } main() { ... stop = 0; threads=0; DosSemClear(&proceed); /* start threads */ ... /* main loop */ while (!stop) ... /* do something */ ... } DosSemClear(&proceed); /* wait for threads to kill themselves */ while (threads); ... } --------------------- But this is still not good enough. If the quit command comes from keyboard, you must still click the mouse, and vice versa. I know no *good* solution. What you need is the ability to kill threads from the outside, and OS/2 does not allow that as far as I know. DosSuspendThread is useless here. Another passable solution would be to have a timeout in MouReadEventQue() and KbdCharIn(). No luck there, either. What you *can* do is to simulate timeouts. This brings back polling, tempered with judicious use of DosSleep(). If you know that the quit command always comes from keyboard (as in uEmacs), then you need to poll only the mouse. This is fortunate, because response time is not so critical with mouse, and because mouse has its own event queue, events do not go unnoticed while we sleep, so we can sleep longer. Here is the "final" version of mou_thread(): #define AWHILE 333L /* third of a second */ mou_thread() { int nEvents; threads++; while (1) { DosSemWait(&proceed,-1L); /* wait for permission to continue */ if (stop) { threads--; DosExit(0,0); } MouGetNumQueEl(...); /* set nEvents to number of events in mouse queue */ if (nEvents==0) { DosSleep(AWHILE); continue; /* restart loop */ } /* translate all mouse events in queue to character strings, store them in FIFO */ ... /* indicate there is something in FIFO */ DosSemClear(&something); DosSemSet(&proceed); } threads--; DosExit(0,0); } Although this works well, THIS IS NOT A GOOD SOLUTION! OS/2 should provide a combined keyboard / mouse event queue for text-mode applications, too. PM has it, of course. And there should be a way to kill threads, as there is a way to kill processes (sessions). Perhaps in a future release... Or perhaps somebody knows how to do it better with the present tools? --- Hannu Kulokari CC, U of Helsinki kulokari@cc.helsinki.fi