chiark / gitweb /
Create readable text `.bas' for each tokenized BASIC `,ffb' file.
[ssr] / StraySrc / Libraries / Steel / c / win
1 /* Title: -> c.win
2  * Purpose: central control of window sytem events.
3  *
4  */
5
6 /*----- Licensing note ----------------------------------------------------*
7  *
8  * This file is part of Straylight's Steel library.
9  *
10  * Steel is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2, or (at your option)
13  * any later version.
14  *
15  * Steel is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Steel.  If not, write to the Free Software Foundation,
22  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23  */
24
25 #define BOOL int
26 #define TRUE 1
27 #define FALSE 0
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdio.h>
32
33 #include "trace.h"
34 #include "os.h"
35 #include "wimp.h"
36 #include "werr.h"
37 #include "wimpt.h"
38 #include "event.h"
39 #include "msgs.h"
40 #include "mem.h"
41 #include "bbc.h"
42 #include "swis.h"
43 #include "menu.h"
44
45 #include "win.h"
46
47 /* -------- Keeping Track of All Windows. -------- */
48
49 typedef struct win__str {
50   struct win__str *next;
51   struct win__str *prev;
52   wimp_w w;
53   win_event_handler proc;
54   void *handle;
55   void *menuh;
56 } win__str;
57
58 #define DUD (-1)
59
60 static win__str *win__allwindows;
61
62 static win__str *win__find(wimp_w w)
63 {
64   win__str *ws=win__allwindows;
65   while (ws)
66   {
67     if (ws->w==w)
68       return (ws);
69     ws=ws->next;
70   }
71   return (0);
72 }
73
74 static void win__register(wimp_w w, win_event_handler proc, void *handle)
75 {
76   BOOL new=FALSE;
77   win__str *ws=win__find(w);
78   if (ws==0)
79   {
80     new=TRUE;
81     if (ws=(win__str *)mem_alloc(sizeof(win__str)),ws==0)
82     {
83       werr
84       (
85         FALSE,
86         msgs_lookup("winNEM:Not enough memory for new event handler.")
87       );
88       return;
89     }
90     ws->next=win__allwindows;
91     ws->prev=0;
92     if (win__allwindows)
93       win__allwindows->prev=ws;
94     win__allwindows=ws;
95     ws->w=w;
96   }
97   ws->proc=proc;
98   ws->handle=handle;
99   if (new)
100     ws->menuh=0;
101 }
102
103 static void win__discard(wimp_w w)
104 {
105   win__str *ws=win__find(w);
106   if (ws)
107   {
108     if (ws->menuh)
109       menu_attach(w,0,0,0,0); /* Try to free the menu */
110     if (ws->prev)
111       ws->prev->next=ws->next;
112     else
113       win__allwindows=ws->next;
114     if (ws->next)
115       ws->next->prev=ws->prev;
116     mem_free(ws);
117   }
118 }
119
120 BOOL win_read_eventhandler(wimp_w w,win_event_handler *p,void **handle)
121 {
122   win__str *ws=win__find(w);
123   if (ws)
124   {
125     *p=ws->proc;
126     *handle=ws->handle;
127   }
128   return (!!ws);
129 }
130
131 /* -------- Claiming Events. -------- */
132
133 void win_register_event_handler(
134   wimp_w w, win_event_handler proc, void *handle)
135 {
136   if (proc == 0) {
137     win__discard(w);
138   } else {
139     win__register(w, proc, handle);
140   };
141 }
142
143 typedef struct unknown_previewer
144 {
145  struct unknown_previewer *link ;
146  win_unknown_event_processor proc ;
147  void *handle ;
148 } unknown_previewer ;
149
150 typedef struct idle_receiver
151 {
152   struct idle_receiver *next;
153   win_idle_claimer proc;
154   int speed;
155   void *handle;
156 }
157 idle_receiver;
158
159 static wimp_w win__idle = DUD;
160 #define win__unknown_flag (('U'<<24)+('N'<<16)+('K'<<8)+'N')
161
162 static wimp_w win__unknown = DUD;
163 static unknown_previewer *win__unknown_previewer_list;
164 static idle_receiver *idleList;
165 static int win__minPoll=2;
166
167 static void win__setPollingSpeed(void)
168 {
169   int s=win__minPoll;
170   idle_receiver *p=idleList;
171   while (p)
172   {
173     if (p->speed<s && p->speed!=win_DONTCARE)
174       s=p->speed;
175     p=p->next;
176   }
177   wimpt_pollingTime(s);
178 }
179
180 void win_idleTime(int time)
181 {
182   win__minPoll=time;
183   win__setPollingSpeed();
184 }
185
186 void win_addIdleClaimer(win_idle_claimer proc,int speed,void *handle)
187 {
188   idle_receiver *new=(idle_receiver *)mem_alloc(sizeof(idle_receiver));
189   if (new)
190   {
191     new->proc=proc;
192     new->handle=handle;
193     new->next=idleList;
194     new->speed=speed;
195     idleList=new;
196   }
197   win__setPollingSpeed();
198 }
199
200 BOOL win_any_idles(void)
201 {
202   return (!!idleList);
203 }
204
205 void win_remove_idle_claimer(win_idle_claimer proc,void *handle)
206 {
207  idle_receiver *block ;
208  for (block = (idle_receiver *) &idleList ;
209       block->next != 0 && (block->next->proc != proc || block->next->handle != handle) ;
210       block = block->next) ;
211  if (block->next != 0)
212  {
213   idle_receiver *b2 = block->next ;
214   block->next = b2->next ;
215   mem_free(b2) ;
216   win__setPollingSpeed();
217  }
218 }
219
220 void win_claim_idle_events(wimp_w w)
221 {
222   tracef1("idle events -> %d\n",w) ;
223   win__idle = w;
224 }
225
226 wimp_w win_idle_event_claimer(void)
227 {
228   return(win__idle);
229 }
230
231 void win_claim_unknown_events(wimp_w w)
232 {
233   win__unknown = w;
234 }
235
236 wimp_w win_unknown_event_claimer(void)
237 {
238   return win__unknown;
239 }
240
241 void win_add_unknown_event_processor(win_unknown_event_processor p,
242                                      void *h)
243 {
244  unknown_previewer *block = mem_alloc(sizeof(unknown_previewer)) ;
245  if (block != 0)
246  {
247   block->link = win__unknown_previewer_list ;
248   block->proc = p ;
249   block->handle = h ;
250   win__unknown_previewer_list = block ;
251  }
252 }
253
254 void win_remove_unknown_event_processor(win_unknown_event_processor p,
255                                         void *h)
256 {
257  unknown_previewer *block ;
258  for (block = (unknown_previewer *) &win__unknown_previewer_list ;
259       block != 0 && (block->link->proc != p || block->link->handle != h) ;
260       block = block->link) ;
261  if (block != 0)
262  {
263   unknown_previewer *b2 = block->link ;
264   block->link = b2->link ;
265   mem_free(b2) ;
266  }
267 }
268
269 /* -------- Menus. -------- */
270
271 void win_setmenuh(wimp_w w, void *handle)
272 {
273   win__str *p = win__find(w);
274   if (p != 0) {p->menuh = handle;};
275 }
276
277 void *win_getmenuh(wimp_w w) /* 0 if not set */
278 {
279   win__str *p = win__find(w);
280   return(p==0 ? 0 : p->menuh);
281 }
282
283 /* -------- Processing Events. -------- */
284
285 /*
286  * void win_broadcast(wimp_eventstr *e)
287  *
288  * Use
289  *  Broadcasts the given event to ALL windows currently active.  Note: this
290  *  works in a similar way to module service calls - a broadcast can be
291  *  'claimed' by changing it to a null event.  This is most useful for
292  *  informing other windows about events (such as a parent window being
293  *  closed), and thus message broadcasts should be used.
294  *
295  * Parameters
296  *  wimp_eventstr *e == the event to broadcast.
297  */
298
299 void win_broadcast(wimp_eventstr *e)
300 {
301   win__str *p=win__allwindows;
302   win__str *q;
303   while (p)
304   {
305     q=p;
306     p=p->next;
307     (q->proc)(e,q->handle);
308   }
309 }
310
311 BOOL win_processevent(wimp_eventstr *e)
312 {
313   /* --- Houston, we have a problem --- *
314    *
315    * Event handlers can continue over several polls, unlike Sapphire where
316    * this sort of convolution is limited to coroutines.  Our lists of
317    * handlers can become `stale' by the time the previous one finishes,
318    * if we're not careful.  We bodge around the problem (bloody Acorn :-( )
319    * by keeping a static serial number, and a local copy.  If the two don't
320    * match, we declare `stale!' and refuse to process any more events.
321    */
322
323
324   static int serial;
325   int cserial=++serial;
326
327   wimp_w w;
328   win__str *p;
329   os_regset r;
330   wimp_wstate s;
331   tracef1("win_processevent %i.\n", e->e);
332   switch (e->e) {
333   case wimp_ENULL:
334     {
335       idle_receiver *idles=idleList;
336       idle_receiver *next;
337       while (idles && cserial==serial) /* Avoid staleness of lists */
338       {
339         next=idles->next;
340         (idles->proc)(idles->handle);
341         idles=next;
342       }
343     }
344     w = win__idle;
345     break;
346   case wimp_EUSERDRAG:
347     w = win__unknown_flag ;
348     if (wimpt_getVersion()>=300)
349       os_swix(XDragASprite_Stop,&r);
350     break;
351   case wimp_EOPEN:
352     wimpt_noerr(wimp_get_wind_state(e->data.o.w,&s));
353     if (wimpt_justChangedMode() || (s.flags & wimp_WCLICK_TOGGLE))
354       win_adjustBox(&e->data.o);
355     /* And continue... */
356   case wimp_EREDRAW: case wimp_ECLOSE:
357   case wimp_EPTRLEAVE: case wimp_EPTRENTER: case wimp_EKEY:
358   case wimp_ESCROLL:
359     w = e->data.o.w;
360     break;
361   case wimp_EBUT:
362     w = e->data.but.m.w;
363     if (w <= (wimp_w) -1) w = win_ICONBAR;
364     if (wimpt_options() & wimpt_ONOWIMPSHADE)
365     {
366       wimp_icon icn;
367       wimpt_noerr(wimp_get_icon_info(e->data.but.m.w,e->data.but.m.i,&icn));
368       if ((icn.flags & 0x001f0000) == 0x001f0000)
369         e->data.but.m.i=-1;
370     }
371     break;
372   case wimp_ESEND:
373   case wimp_ESENDWANTACK:
374     switch (e->data.msg.hdr.action) {
375     case wimp_MCLOSEDOWN:
376       tracef0("closedown message\n");
377       exit(0);
378     case wimp_MDATALOAD:
379     case wimp_MDATASAVE:
380       tracef1("data %s message arriving.\n",
381              (int) (e->data.msg.hdr.action==wimp_MDATASAVE ? "save" : "load"));
382       if (e->data.msg.data.dataload.w < 0)
383       {
384         tracef0("data message to the icon bar.\n");
385         w = win_ICONBARLOAD ;
386       } else {
387         w = e->data.msg.data.dataload.w;
388       };
389       break;
390     case wimp_MHELPREQUEST:
391       tracef1("help request for window %i.\n", e->data.msg.data.helprequest.m.w);
392       w = e->data.msg.data.helprequest.m.w;
393       if (w < 0) w = win_ICONBARLOAD;
394       break;
395     default:
396       tracef1("unknown message type %i arriving.\n", e->data.msg.hdr.action);
397       w = win__unknown_flag;
398       if (w < 0) w = win_ICONBARLOAD;
399     };
400     break;
401   default:
402     w = win__unknown_flag;
403   };
404
405   if (w==win__unknown_flag || win__find(w) == 0)
406   {
407    unknown_previewer *pr=win__unknown_previewer_list;
408    unknown_previewer *next;
409    while (pr != 0 && cserial==serial)  /* Avoid staleness of lists */
410    {
411     next=pr->link;
412     if (pr->proc(e, pr->handle)) return TRUE ;
413     pr=next;
414    }
415    w = win__unknown ;
416   }
417
418   p = (w == DUD ? 0 : win__find(w));
419   if (p != 0) {
420     p->proc(e, p->handle);
421     return TRUE;
422   } else {
423     return FALSE;
424   };
425 }
426
427 /* -------- Termination. -------- */
428
429 static int win__active;
430
431 void win_activeinc(void)
432 {
433   win__active++;
434 }
435
436 void win_activedec(void)
437 {
438   win__active--;
439 }
440
441 int win_activeno(void)
442 {
443   return win__active;
444 }
445
446 /* -------- Giving away the caret. -------- */
447
448 void win_give_away_caret(void)
449 {
450   win__str *ws=win__allwindows;
451   while (ws) {
452     if (ws->w != DUD) { /* found a window */
453       wimp_wstate s;
454       wimp_eventstr e;
455       tracef1("get state of window %i.", ws->w);
456       (void) wimp_get_wind_state(ws->w, &s);
457       tracef2("behind=%i flags=%i.\n", s.o.behind, s.flags);
458       if (s.o.behind == DUD && (s.flags & wimp_WOPEN) != 0) {
459         /* w is the top window */
460         /* if it wants the caret, it will grab it. */
461         tracef0("Opening it.\n");
462         e.e = wimp_EOPEN;
463         e.data.o = s.o;
464         wimpt_fake_event(&e);
465         break;
466       };
467     };
468     ws=ws->next;
469   };
470 }
471
472 /* ----------- setting a window title ------------ */
473
474 void win_settitle(wimp_w w, char *newtitle,...)
475 {
476   wimp_redrawstr r;
477   va_list ap;
478   wimp_winfo *winfo;
479
480   va_start(ap,newtitle);
481   if((winfo = mem_alloc(sizeof(wimp_wind) + 200*sizeof(wimp_icon))) == 0)
482   {
483     werr(FALSE, msgs_lookup("win2:Not enough memory to change window title."));
484     va_end(ap);
485     return;
486   }
487
488 tracef1("New title is %s\n", (int)newtitle);
489   /* --- get the window's details --- */
490     winfo->w = w;
491     wimpt_noerr(wimp_get_wind_info(winfo));
492
493   /* --- put the new title string in the title icon's buffer --- */
494 tracef1("Lib buffer is at %d\n", (int)winfo->info.title.indirecttext.buffer);
495     vsprintf(winfo->info.title.indirecttext.buffer, newtitle, ap);
496     va_end(ap);
497
498   /* --- invalidate the title bar in absolute coords --- */
499     if (winfo->info.flags & wimp_WOPEN)
500     {
501       r.w = (wimp_w) -1;    /* absolute screen coords */
502       r.box = winfo->info.box;
503       r.box.y1 += 36;            /* yuk - tweaky */
504       r.box.y0 = r.box.y1 - 36;
505       wimpt_noerr(wimp_force_redraw(&r));
506     }
507
508   /* --- free space used to window info --- */
509     mem_free(winfo);
510 }
511
512 /*
513  * void win_gadgetWidths(wimp_wflags f,wimp_box *b)
514  *
515  * Use
516  *  Returns a box giving the width of the system area around a window with
517  *  window flags f.
518  */
519
520 void win_gadgetWidths(wimp_wflags f,wimp_box *b)
521 {
522   wimp_wind w=
523   {
524     {-500,-500,-300,-300},0,0,-1,
525     0,
526     {7,2,7,1,3,1,12},
527     {0,0,100,100},
528     0,
529     0,
530     (void *)1,
531     0,
532     {"anywindow"},
533     0
534   };
535   wimp_w temp;
536   wimp_wstate s;
537   wimp_redrawstr r;
538
539   w.flags=f | wimp_WTRESPASS;
540   if (wimp_create_wind(&w,&temp))
541   {
542     b->x0=0;
543     b->x1=(f & wimp_WHSCR) ? 40 : 0;
544     b->y0=(f & wimp_WVSCR) ? 40 : 0;
545     b->y1=(f & wimp_WTITLE) ? 40 : 0;
546   }
547   else
548   {
549     wimpt_noerr(wimp_get_wind_state(temp,&s));
550     wimpt_noerr(wimp_open_wind(&s.o));
551     wimpt_noerr(wimp_get_wind_state(temp,&s));
552     r.w=temp;
553     wimpt_noerr(wimp_getwindowoutline(&r));
554     b->x0=s.o.box.x0-r.box.x0;
555     b->x1=r.box.x1-s.o.box.x1;
556     b->y1=r.box.y1-s.o.box.y1;
557     b->y0=s.o.box.y0-r.box.y0;
558     wimpt_noerr(wimp_delete_wind(temp));
559   }
560 }
561
562 /*
563  * void win_adjustBox(wimp_openstr *o)
564  *
565  * Use
566  *  Adjusts a window so that it fits on the screen.
567  *
568  * Parameters
569  *  wimp_openstr *o == pointer to the block to fiddle
570  */
571
572 void win_adjustBox(wimp_openstr *o)
573 {
574   int scx=wimpt_scwidth();
575   int scy=wimpt_scheight();
576   int w=o->box.x1-o->box.x0;
577   int h=o->box.y1-o->box.y0;
578   wimp_box b;
579   wimp_wstate s;
580   wimpt_noerr(wimp_get_wind_state(o->w,&s));
581   win_gadgetWidths(s.flags,&b);
582
583   if (o->box.x1-o->box.x0>scx-b.x0-b.x1)
584   {
585     o->box.x0=b.x0;
586     o->box.x1=scx-b.x1;
587   }
588   else if (o->box.x1>scx-b.x1)
589   {
590     o->box.x1=scx-b.x1;
591     o->box.x0=scx-b.x1-w;
592   }
593   else if (o->box.x0<b.x0)
594   {
595     o->box.x0=b.x0;
596     o->box.x1=b.x0+w;
597   }
598
599   if (o->box.y1-o->box.y0>scy-b.y0-b.y1)
600   {
601     o->box.y0=b.y0;
602     o->box.y1=scy-b.y1;
603   }
604   else if (o->box.y1>scy-b.y1)
605   {
606     o->box.y1=scy-b.y1;
607     o->box.y0=scy-b.y1-h;
608   }
609   else if (o->box.y0<b.y0)
610   {
611     o->box.y0=b.y0;
612     o->box.y1=b.y0+h;
613   }
614 }
615
616 /*
617  * BOOL win_anyWindows(void)
618  *
619  * Use
620  *  Informs the caller if there are any windows left.
621  *
622  * Returns
623  *  TRUE if there are.
624  */
625
626 BOOL win_anyWindows(void)
627 {
628   wimp_wind w=
629   {
630     {0,0,10,10},0,0,-1,
631     0,
632     {7,2,7,1,3,1,12},
633     {0,0,10,10},
634     0,
635     0,
636     (void *)1,
637     0,
638     {"anywindow"},
639     0
640   };
641   os_error *err;
642   wimp_w handle;
643   err=wimp_create_wind(&w,&handle);
644   if (err)
645     return (FALSE);
646   else
647   {
648     wimp_delete_wind(handle);
649     return (TRUE);
650   }
651 }
652
653 /* end */