chiark / gitweb /
Initial revision
[ssr] / StraySrc / Libraries / Steel / c / event
1 /*
2  * event.c
3  *
4  * Handling and dispatching events
5  *
6  * © 1994-1998 Straylight
7  */
8
9 /*----- Licensing note ----------------------------------------------------*
10  *
11  * This file is part of Straylight's Steel library.
12  *
13  * Steel is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2, or (at your option)
16  * any later version.
17  *
18  * Steel is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with Steel.  If not, write to the Free Software Foundation,
25  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "event.h"
34 #include "wimpt.h"
35 #include "win.h"
36 #include "menu.h"
37 #include "msgs.h"
38 #include "werr.h"
39 #include "alarm.h"
40 #include "mem.h"
41 #include "swis.h"
42
43 #define dbox__INTERNALS
44 #include "dbox.h"
45
46 /*----- Global variables --------------------------------------------------*/
47
48 static wimp_emask event__mask=wimp_EMNULL;
49
50 static menu event__currentMenu;
51 static event_menu_maker event__currentMaker;
52 static event_menu_proc event__menuProc;
53 static void *event__menuHandle;
54
55 /* --- For bodging menus which move around in memory --- */
56
57 static int event__menuX;
58 static int event__menuY;
59
60 static BOOL event__recreating;
61 static event_menuPurpose event__purpose;
62 static BOOL event__returnMenuHelp;
63
64 /*----- Event masks -------------------------------------------------------*/
65
66 void event_setmask (wimp_emask mask)
67 {
68   event__mask = mask;
69 }
70
71 wimp_emask event_getmask (void)
72 {
73   if (win_any_idles())
74     return (event__mask&~wimp_EMNULL);
75   else
76     return (event__mask);
77 }
78
79 /*----- Menu button handling ----------------------------------------------*/
80
81 typedef struct event__midbstr
82 {
83   event_midbhandler proc;
84   int gadget;
85   void *handlea;
86   void *handleb;
87   void *handlec;
88 }
89 event__midbstr;
90
91 /*
92  * BOOL event_attachMidbHandler(wimp_w w,
93  *                              event_midbhandler proc,
94  *                              int gadget,
95  *                              void *handlea,
96  *                              void *handleb,
97  *                              void *handlec)
98  *
99  * Use
100  *  Registers a function to be called by event when a menu button event is
101  *  received by a particular window.  Typically, this handler will display a
102  *  menu at the mouse coordinates, although you can do anything you want.
103  *
104  *  You shouldn't need to call this function very much.  It's really for
105  *  allowing other systems to supply menu attachment functions like event_-
106  *  attachmenu etc.
107  *
108  * Parameters
109  *  wimp_w == the window to which to attach the function
110  *  event_midbhandler proc == the function to call when a menu button event
111  *    is received for the window
112  *  others == arguments to be passed (unprocessed) to proc when it's called.
113  *
114  * Returns
115  *  TRUE if the attachment succeeded.
116  */
117
118 BOOL event_attachMidbHandler(wimp_w w,
119                              event_midbhandler proc,
120                              int gadget,
121                              void *handlea,
122                              void *handleb,
123                              void *handlec)
124 {
125   event__midbstr *e=win_getmenuh(w);
126   if (!e && proc)
127   {
128     if (e=mem_alloc(sizeof(event__midbstr)),!e)
129     {
130       werr(FALSE,msgs_lookup("eventNMAM:Not enough memory to attach menu"));
131       return (FALSE);
132     }
133     win_setmenuh(w,e);
134   }
135   if (proc)
136   {
137     e->proc=proc;
138     e->gadget=gadget;
139     e->handlea=handlea;
140     e->handleb=handleb;
141     e->handlec=handlec;
142   }
143   else if (e)
144   {
145     mem_free(e);
146     win_setmenuh(w,0);
147   }
148   return (TRUE);
149 }
150
151 void event_attachedMenu(wimp_w w,
152                         menu *m,
153                         event_menu_maker *mk,
154                         event_menu_proc *proc,
155                         void **handle)
156 {
157   event__midbstr *e=win_getmenuh(w);
158   if (e)
159   {
160     *m=(e->gadget ? 0 : e->handlea);
161     *mk=(e->gadget ? (event_menu_maker)e->handlea : 0);
162     *proc=(event_menu_proc)e->handleb;
163     *handle=e->handlec;
164   }
165   else
166   {
167     *m=0;
168     *mk=0;
169     *proc=0;
170     *handle=0;
171   }
172 }
173
174 /*----- Menu displaying ---------------------------------------------------*/
175
176 /*
177  * void event__displayMenu(void)
178  *
179  * Use
180  *  Displays (or redisplays) the current menu.  It is assumed that the menu
181  *  maker has been called to set up the current menu pointer.
182  */
183
184 static void event__displayMenu(void)
185 {
186   wimp_menustr *m;
187   m=menu_syshandle(event__currentMenu);
188   if (win_anyWindows()==FALSE)
189   {
190     werr(FALSE,
191          msgs_lookup("eventTMWM:Too many windows - "
192                                "%s could not create menu."),
193          wimpt_programname());
194   }
195   else
196     wimpt_noerr(wimp_create_menu(m,event__menuX,event__menuY));
197 }
198
199 /*
200  * void event__findMenu(BOOL iconbar)
201  *
202  * Use
203  *  Sets up the global event__menuX and event__menuY for the current menu
204  *  for a subsequent call of event__displayMenu.  Also (as a side-effect)
205  *  calls the menu maker.
206  *
207  * Parameters
208  *  BOOL iconbar == whether the menu has been opened from the iconbar
209  */
210
211 static void event__findMenu(BOOL iconbar)
212 {
213   wimp_mousestr m;
214   wimp_menustr *mn;
215   wimp_menuitem *it;
216   int height;
217   if (event__currentMaker)
218   {
219     event__recreating=FALSE;
220     event__currentMenu=event__currentMaker(event__menuHandle);
221   }
222   wimpt_noerr(wimp_get_point_info(&m));
223   event__menuX=m.x-64;
224   if (iconbar)
225   {
226     mn=menu_syshandle(event__currentMenu);
227     height=0;
228     it=(wimp_menuitem *)(mn+1);
229     for (;;)
230     {
231       height+=44; /* I know it may not be, but it should. */
232       if (it->flags & wimp_MSEPARATE)
233         height+=24;
234       if (it->flags & wimp_MLAST)
235         break;
236       it++;
237     }
238     event__menuY=96+height;
239   }
240   else
241     event__menuY=m.y;
242 }
243
244 /*
245  * void event_openMenu(menu it,event_menu_proc proc,void *handle)
246  * void event_makeMenu(event_menu_maker it,event_menu_proc proc,void *handle)
247  * void event_openIconbarMenu(...)
248  * void event_makeIconbarMenu(...)
249  *
250  * Use
251  *  Opens a menu onto the screen and tells your routine when it's finished.
252  *
253  * Parameters
254  *  menu it == the menu or menu maker procedure
255  *  event_menu_proc proc == the menu handler function
256  *  void *handle == it's jolly old handle
257  */
258
259 void event_openMenu(menu it,event_menu_proc proc,void *handle)
260 {
261   event__currentMenu=it;
262   event__currentMaker=0;
263   event__menuProc=proc;
264   event__menuHandle=handle;
265   event__findMenu(FALSE);
266   event__displayMenu();
267 }
268
269 void event_makeMenu(event_menu_maker maker,event_menu_proc proc,void *handle)
270 {
271   event__currentMaker=maker;
272   event__menuProc=proc;
273   event__menuHandle=handle;
274   event__findMenu(FALSE);
275   event__displayMenu();
276 }
277
278 void event_openIconbarMenu(menu it,event_menu_proc proc,void *handle)
279 {
280   event__currentMenu=it;
281   event__currentMaker=0;
282   event__menuProc=proc;
283   event__menuHandle=handle;
284   event__findMenu(TRUE);
285   event__displayMenu();
286 }
287
288 void event_makeIconbarMenu(event_menu_maker maker,
289                            event_menu_proc proc,
290                            void *handle)
291 {
292   event__currentMaker=maker;
293   event__menuProc=proc;
294   event__menuHandle=handle;
295   event__findMenu(TRUE);
296   event__displayMenu();
297 }
298
299 /*----- Menu attachment ---------------------------------------------------*/
300
301 /*
302  * event__defaultmidb(wimp_w w,
303  *                    int made,
304  *                    void *handlea,
305  *                    void *handleb,
306  *                    void *handlec)
307  *
308  * Use
309  *  Default menu button handler for windows (to support event_attachmenu
310  *  etc.)
311  *
312  * Parameters
313  *  As for an event_midbhandler.
314  */
315
316 static void event__defaultmidb(wimp_w w,
317                                int made,
318                                void *handlea,
319                                void *handleb,
320                                void *handlec)
321 {
322   if (w==win_ICONBAR)
323   {
324     if (!made)
325     {
326       event_openIconbarMenu((menu)handlea,
327                             (event_menu_proc)handleb,
328                             handlec);
329     }
330     else
331     {
332       event_makeIconbarMenu((event_menu_maker)handlea,
333                             (event_menu_proc)handleb,
334                             handlec);
335     }
336   }
337   else
338   {
339     if (!made)
340     {
341       event_openMenu((menu)handlea,
342                      (event_menu_proc)handleb,
343                      handlec);
344     }
345     else
346     {
347       event_makeMenu((event_menu_maker)handlea,
348                      (event_menu_proc)handleb,
349                      handlec);
350     }
351   }
352 }
353
354 /*----- Default menu attachment -------------------------------------------*/
355
356 /*
357  * void event_attachmenu(wimp_w w,menu m,event_menu_proc p,void *handle)
358  *
359  * Use
360  *  Attaches a menu to a window so that it opens when you click menu on it.
361  *
362  * Parameters
363  *  wimp_w w == the window to attach to
364  *  menu m == the menu to attach to it
365  *  event_menu_proc p == what to do when a menu item is chosen
366  *  void *handle == something else to send to p
367  */
368
369 BOOL event_attachmenu(wimp_w w,menu m,event_menu_proc p,void *handle)
370 {
371   return (event_attachMidbHandler(w,
372                                   event__defaultmidb,
373                                   FALSE,
374                                   m,
375                                   (void *)p,
376                                   handle));
377 }
378
379 /*
380  * void event_attachmenumaker(wimp_w w,
381  *                            event_menu_maker m,
382  *                            event_menu_proc p,
383  *                            void *handle)
384  *
385  * Use
386  *  Attaches a menu to a window so that it opens when you click menu on it.
387  *
388  * Parameters
389  *  wimp_w w == the window to attach to
390  *  event_menu_maker m == how to create the menu
391  *  event_menu_proc p == what to do when a menu item is chosen
392  *  void *handle == something else to send to p
393  */
394
395 BOOL event_attachmenumaker(wimp_w w,
396                            event_menu_maker m,
397                            event_menu_proc p,
398                            void *handle)
399 {
400   return (event_attachMidbHandler(w,
401                                   event__defaultmidb,
402                                   TRUE,
403                                   (void *)m,
404                                   (void *)p,
405                                   handle));
406 }
407
408 /*----- Pointless window counting -----------------------------------------*/
409
410 BOOL event_anywindows(void)
411 {
412   return(win_activeno() != 0);
413 }
414
415 /*----- Event processing -- the interesting bit ---------------------------*/
416
417 void event_returnMenuHelp(BOOL doit)
418 {
419   event__returnMenuHelp=doit;
420 }
421
422 event_menuPurpose event_whyMenuEvent(void)
423 {
424   return (event__purpose);
425 }
426
427 BOOL event_is_menu_being_recreated(void)
428 {
429  return event__recreating;
430 }
431
432 void (event_process)(void)
433 {
434   event_process();
435 }
436 /*
437  * void event__menuHit(int *hits)
438  *
439  * Use
440  *  Sends a menu hit array to the current menu handling procedure
441  *
442  * Parameters
443  *  int *hits == an array of menu hits (WIMP-style) to convert to obscure
444  *    RISC_OSLib format (1-indexed :-( ) and bundled to an event handler.
445  */
446
447 static void event__menuHit(int *hits)
448 {
449   int i;
450   char buff[9]; /* Silly event -- using chars! */
451   if (event__currentMenu)
452   {
453     for (i=0;hits[i]!=-1;i++)
454       buff[i]=hits[i]+1;
455     buff[i]=hits[i]+1;
456     event__menuProc(event__menuHandle,buff);
457   }
458 }
459
460 /*
461  * void event__process(void)
462  *
463  * Use
464  *  Processes events :-)
465  */
466
467 void event__process(void)
468 {
469   wimp_eventstr e;
470   int i;
471   static BOOL lastWasMenu;
472   int nextAlarm;
473
474   /* --- Terminate the app if it has no windows --- */
475
476   if (!event_anywindows())
477     exit(0);
478
479   /* --- If we just clicked on a menu, we may need to reopen it --- */
480
481   if (lastWasMenu &&
482       event__currentMenu &&
483       wimpt_last_event()->e==wimp_EMENU)
484   {
485     wimp_mousestr m;
486     wimpt_noerr(wimp_get_point_info(&m));
487     if (m.bbits & wimp_BRIGHT)
488     {
489       if (event__currentMaker)
490       {
491         event__recreating=TRUE;
492         event__currentMenu=event__currentMaker(event__menuHandle);
493       }
494       event__displayMenu();
495     }
496     else
497     {
498       i=-1;
499       event__purpose=event_MENUDELETE;
500       event__menuHit(&i);
501     }
502   }
503
504   /* --- Get an event from wimpt and process it --- */
505
506   wimpt_noerr(wimpt_poll(event_getmask(),&e));
507   lastWasMenu=FALSE;
508
509   switch (e.e)
510   {
511     case wimp_ENULL:
512       while (alarm_next(&nextAlarm) && alarm_timenow()-nextAlarm>=0)
513         alarm_callnext();
514       break;
515     case wimp_EBUT:
516       if (e.data.but.m.bbits & wimp_BMID)
517       {
518         wimp_w w=(e.data.but.m.w<=-1) ? win_ICONBAR : e.data.but.m.w;
519         event__midbstr *m=win_getmenuh(w);
520         if (m)
521         {
522           (m->proc)(w,m->gadget,m->handlea,m->handleb,m->handlec);
523           return;
524         }
525       }
526       break;
527     case wimp_EMENU:
528       event__purpose=event_MENUSELECT;
529       event__menuHit(e.data.menu);
530       lastWasMenu=TRUE;
531       break;
532     case wimp_ESEND:
533     case wimp_ESENDWANTACK:
534       switch (e.data.msg.hdr.action)
535       {
536
537         case wimp_MMENUWARN:
538           if (e.data.msg.data.words[0]>0x8000)
539           {
540             if (win_anyWindows()==FALSE)
541             {
542               werr(FALSE,
543                    msgs_lookup("eventTMWS:Too many windows - "
544                                          "%s could not create submenu."),
545                    wimpt_programname());
546             }
547             else
548             {
549               wimpt_noerr(wimp_create_submenu(
550                                (wimp_menustr *)e.data.msg.data.words[0],
551                                e.data.msg.data.words[1],
552                                e.data.msg.data.words[2]));
553             }
554           }
555           else
556           {
557             for (i=3;e.data.msg.data.words[i] != -1;i++)
558               /* blank! */;
559             e.data.msg.data.words[i++] = -1;
560             event__purpose=event_MENUSUBMENU;
561             event__menuHit(e.data.msg.data.words+3);
562           }
563           break;
564         case wimp_MHELPREQUEST:
565           if (event__returnMenuHelp &&
566               e.data.msg.data.helprequest.m.w!=dbox__menuDboxWindow())
567           {
568             int buff[20];
569
570             wimpt_noerr(wimp_getmenustate(1,
571                                           buff,
572                                           e.data.msg.data.helprequest.m.w,
573                                           e.data.msg.data.helprequest.m.i));
574             if (buff[0]!=-1)
575             {
576               event__purpose=event_MENUHELP;
577               event__menuHit(buff);
578             }
579           }
580           break;
581         case 0x400C9:        /* Message_MenusDeleted */
582           e.e=wimp_EMENU;
583           e.data.menu[0]=-1;
584           event__purpose=event_MENUDELETE;
585           event__menuHit(e.data.menu);
586           break;
587       }
588       break;
589   }
590
591   if (!win_processevent(&e))
592   {
593
594     /* --- Default event handling --- */
595
596     switch (e.e)
597     {
598       case wimp_EREDRAW:
599         werr(TRUE,msgs_lookup("Internal error: unserviced redraw event"));
600         break;
601       case wimp_EOPEN:
602         wimpt_noerr(wimp_open_wind(&e.data.o));
603         break;
604       case wimp_EKEY:
605         wimp_processkey(e.data.key.chcode);
606         break;
607     }
608   }
609 }
610
611 void event_clear_current_menu(void)
612 {
613   wimpt_noerr(wimp_create_menu((wimp_menustr*)-1,0,0));
614   if (event__currentMenu)
615   {
616     int i=-1;
617     event__purpose=event_MENUDELETE;
618     event__menuHit(&i);
619     event__currentMenu=0;
620   }
621 }