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 }