Initial revision
[ssr] / StraySrc / Libraries / Steel / c / win
CommitLineData
2ee739cc 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
49typedef 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
60static win__str *win__allwindows;
61
62static 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
74static 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
103static 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
120BOOL 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
133void 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
143typedef struct unknown_previewer
144{
145 struct unknown_previewer *link ;
146 win_unknown_event_processor proc ;
147 void *handle ;
148} unknown_previewer ;
149
150typedef struct idle_receiver
151{
152 struct idle_receiver *next;
153 win_idle_claimer proc;
154 int speed;
155 void *handle;
156}
157idle_receiver;
158
159static wimp_w win__idle = DUD;
160#define win__unknown_flag (('U'<<24)+('N'<<16)+('K'<<8)+'N')
161
162static wimp_w win__unknown = DUD;
163static unknown_previewer *win__unknown_previewer_list;
164static idle_receiver *idleList;
165static int win__minPoll=2;
166
167static 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
180void win_idleTime(int time)
181{
182 win__minPoll=time;
183 win__setPollingSpeed();
184}
185
186void 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
200BOOL win_any_idles(void)
201{
202 return (!!idleList);
203}
204
205void 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
220void win_claim_idle_events(wimp_w w)
221{
222 tracef1("idle events -> %d\n",w) ;
223 win__idle = w;
224}
225
226wimp_w win_idle_event_claimer(void)
227{
228 return(win__idle);
229}
230
231void win_claim_unknown_events(wimp_w w)
232{
233 win__unknown = w;
234}
235
236wimp_w win_unknown_event_claimer(void)
237{
238 return win__unknown;
239}
240
241void 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
254void 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
271void win_setmenuh(wimp_w w, void *handle)
272{
273 win__str *p = win__find(w);
274 if (p != 0) {p->menuh = handle;};
275}
276
277void *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
299void 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
311BOOL 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
429static int win__active;
430
431void win_activeinc(void)
432{
433 win__active++;
434}
435
436void win_activedec(void)
437{
438 win__active--;
439}
440
441int win_activeno(void)
442{
443 return win__active;
444}
445
446/* -------- Giving away the caret. -------- */
447
448void 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
474void 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
488tracef1("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 --- */
494tracef1("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
520void 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
572void 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
626BOOL 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 */