/* Title: -> c.win * Purpose: central control of window sytem events. * */ /*----- Licensing note ----------------------------------------------------* * * This file is part of Straylight's Steel library. * * Steel is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * Steel is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Steel. If not, write to the Free Software Foundation, * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define BOOL int #define TRUE 1 #define FALSE 0 #include #include #include #include #include "trace.h" #include "os.h" #include "wimp.h" #include "werr.h" #include "wimpt.h" #include "event.h" #include "msgs.h" #include "mem.h" #include "bbc.h" #include "swis.h" #include "menu.h" #include "win.h" /* -------- Keeping Track of All Windows. -------- */ typedef struct win__str { struct win__str *next; struct win__str *prev; wimp_w w; win_event_handler proc; void *handle; void *menuh; } win__str; #define DUD (-1) static win__str *win__allwindows; static win__str *win__find(wimp_w w) { win__str *ws=win__allwindows; while (ws) { if (ws->w==w) return (ws); ws=ws->next; } return (0); } static void win__register(wimp_w w, win_event_handler proc, void *handle) { BOOL new=FALSE; win__str *ws=win__find(w); if (ws==0) { new=TRUE; if (ws=(win__str *)mem_alloc(sizeof(win__str)),ws==0) { werr ( FALSE, msgs_lookup("winNEM:Not enough memory for new event handler.") ); return; } ws->next=win__allwindows; ws->prev=0; if (win__allwindows) win__allwindows->prev=ws; win__allwindows=ws; ws->w=w; } ws->proc=proc; ws->handle=handle; if (new) ws->menuh=0; } static void win__discard(wimp_w w) { win__str *ws=win__find(w); if (ws) { if (ws->menuh) menu_attach(w,0,0,0,0); /* Try to free the menu */ if (ws->prev) ws->prev->next=ws->next; else win__allwindows=ws->next; if (ws->next) ws->next->prev=ws->prev; mem_free(ws); } } BOOL win_read_eventhandler(wimp_w w,win_event_handler *p,void **handle) { win__str *ws=win__find(w); if (ws) { *p=ws->proc; *handle=ws->handle; } return (!!ws); } /* -------- Claiming Events. -------- */ void win_register_event_handler( wimp_w w, win_event_handler proc, void *handle) { if (proc == 0) { win__discard(w); } else { win__register(w, proc, handle); }; } typedef struct unknown_previewer { struct unknown_previewer *link ; win_unknown_event_processor proc ; void *handle ; } unknown_previewer ; typedef struct idle_receiver { struct idle_receiver *next; win_idle_claimer proc; int speed; void *handle; } idle_receiver; static wimp_w win__idle = DUD; #define win__unknown_flag (('U'<<24)+('N'<<16)+('K'<<8)+'N') static wimp_w win__unknown = DUD; static unknown_previewer *win__unknown_previewer_list; static idle_receiver *idleList; static int win__minPoll=2; static void win__setPollingSpeed(void) { int s=win__minPoll; idle_receiver *p=idleList; while (p) { if (p->speedspeed!=win_DONTCARE) s=p->speed; p=p->next; } wimpt_pollingTime(s); } void win_idleTime(int time) { win__minPoll=time; win__setPollingSpeed(); } void win_addIdleClaimer(win_idle_claimer proc,int speed,void *handle) { idle_receiver *new=(idle_receiver *)mem_alloc(sizeof(idle_receiver)); if (new) { new->proc=proc; new->handle=handle; new->next=idleList; new->speed=speed; idleList=new; } win__setPollingSpeed(); } BOOL win_any_idles(void) { return (!!idleList); } void win_remove_idle_claimer(win_idle_claimer proc,void *handle) { idle_receiver *block ; for (block = (idle_receiver *) &idleList ; block->next != 0 && (block->next->proc != proc || block->next->handle != handle) ; block = block->next) ; if (block->next != 0) { idle_receiver *b2 = block->next ; block->next = b2->next ; mem_free(b2) ; win__setPollingSpeed(); } } void win_claim_idle_events(wimp_w w) { tracef1("idle events -> %d\n",w) ; win__idle = w; } wimp_w win_idle_event_claimer(void) { return(win__idle); } void win_claim_unknown_events(wimp_w w) { win__unknown = w; } wimp_w win_unknown_event_claimer(void) { return win__unknown; } void win_add_unknown_event_processor(win_unknown_event_processor p, void *h) { unknown_previewer *block = mem_alloc(sizeof(unknown_previewer)) ; if (block != 0) { block->link = win__unknown_previewer_list ; block->proc = p ; block->handle = h ; win__unknown_previewer_list = block ; } } void win_remove_unknown_event_processor(win_unknown_event_processor p, void *h) { unknown_previewer *block ; for (block = (unknown_previewer *) &win__unknown_previewer_list ; block != 0 && (block->link->proc != p || block->link->handle != h) ; block = block->link) ; if (block != 0) { unknown_previewer *b2 = block->link ; block->link = b2->link ; mem_free(b2) ; } } /* -------- Menus. -------- */ void win_setmenuh(wimp_w w, void *handle) { win__str *p = win__find(w); if (p != 0) {p->menuh = handle;}; } void *win_getmenuh(wimp_w w) /* 0 if not set */ { win__str *p = win__find(w); return(p==0 ? 0 : p->menuh); } /* -------- Processing Events. -------- */ /* * void win_broadcast(wimp_eventstr *e) * * Use * Broadcasts the given event to ALL windows currently active. Note: this * works in a similar way to module service calls - a broadcast can be * 'claimed' by changing it to a null event. This is most useful for * informing other windows about events (such as a parent window being * closed), and thus message broadcasts should be used. * * Parameters * wimp_eventstr *e == the event to broadcast. */ void win_broadcast(wimp_eventstr *e) { win__str *p=win__allwindows; win__str *q; while (p) { q=p; p=p->next; (q->proc)(e,q->handle); } } BOOL win_processevent(wimp_eventstr *e) { /* --- Houston, we have a problem --- * * * Event handlers can continue over several polls, unlike Sapphire where * this sort of convolution is limited to coroutines. Our lists of * handlers can become `stale' by the time the previous one finishes, * if we're not careful. We bodge around the problem (bloody Acorn :-( ) * by keeping a static serial number, and a local copy. If the two don't * match, we declare `stale!' and refuse to process any more events. */ static int serial; int cserial=++serial; wimp_w w; win__str *p; os_regset r; wimp_wstate s; tracef1("win_processevent %i.\n", e->e); switch (e->e) { case wimp_ENULL: { idle_receiver *idles=idleList; idle_receiver *next; while (idles && cserial==serial) /* Avoid staleness of lists */ { next=idles->next; (idles->proc)(idles->handle); idles=next; } } w = win__idle; break; case wimp_EUSERDRAG: w = win__unknown_flag ; if (wimpt_getVersion()>=300) os_swix(XDragASprite_Stop,&r); break; case wimp_EOPEN: wimpt_noerr(wimp_get_wind_state(e->data.o.w,&s)); if (wimpt_justChangedMode() || (s.flags & wimp_WCLICK_TOGGLE)) win_adjustBox(&e->data.o); /* And continue... */ case wimp_EREDRAW: case wimp_ECLOSE: case wimp_EPTRLEAVE: case wimp_EPTRENTER: case wimp_EKEY: case wimp_ESCROLL: w = e->data.o.w; break; case wimp_EBUT: w = e->data.but.m.w; if (w <= (wimp_w) -1) w = win_ICONBAR; if (wimpt_options() & wimpt_ONOWIMPSHADE) { wimp_icon icn; wimpt_noerr(wimp_get_icon_info(e->data.but.m.w,e->data.but.m.i,&icn)); if ((icn.flags & 0x001f0000) == 0x001f0000) e->data.but.m.i=-1; } break; case wimp_ESEND: case wimp_ESENDWANTACK: switch (e->data.msg.hdr.action) { case wimp_MCLOSEDOWN: tracef0("closedown message\n"); exit(0); case wimp_MDATALOAD: case wimp_MDATASAVE: tracef1("data %s message arriving.\n", (int) (e->data.msg.hdr.action==wimp_MDATASAVE ? "save" : "load")); if (e->data.msg.data.dataload.w < 0) { tracef0("data message to the icon bar.\n"); w = win_ICONBARLOAD ; } else { w = e->data.msg.data.dataload.w; }; break; case wimp_MHELPREQUEST: tracef1("help request for window %i.\n", e->data.msg.data.helprequest.m.w); w = e->data.msg.data.helprequest.m.w; if (w < 0) w = win_ICONBARLOAD; break; default: tracef1("unknown message type %i arriving.\n", e->data.msg.hdr.action); w = win__unknown_flag; if (w < 0) w = win_ICONBARLOAD; }; break; default: w = win__unknown_flag; }; if (w==win__unknown_flag || win__find(w) == 0) { unknown_previewer *pr=win__unknown_previewer_list; unknown_previewer *next; while (pr != 0 && cserial==serial) /* Avoid staleness of lists */ { next=pr->link; if (pr->proc(e, pr->handle)) return TRUE ; pr=next; } w = win__unknown ; } p = (w == DUD ? 0 : win__find(w)); if (p != 0) { p->proc(e, p->handle); return TRUE; } else { return FALSE; }; } /* -------- Termination. -------- */ static int win__active; void win_activeinc(void) { win__active++; } void win_activedec(void) { win__active--; } int win_activeno(void) { return win__active; } /* -------- Giving away the caret. -------- */ void win_give_away_caret(void) { win__str *ws=win__allwindows; while (ws) { if (ws->w != DUD) { /* found a window */ wimp_wstate s; wimp_eventstr e; tracef1("get state of window %i.", ws->w); (void) wimp_get_wind_state(ws->w, &s); tracef2("behind=%i flags=%i.\n", s.o.behind, s.flags); if (s.o.behind == DUD && (s.flags & wimp_WOPEN) != 0) { /* w is the top window */ /* if it wants the caret, it will grab it. */ tracef0("Opening it.\n"); e.e = wimp_EOPEN; e.data.o = s.o; wimpt_fake_event(&e); break; }; }; ws=ws->next; }; } /* ----------- setting a window title ------------ */ void win_settitle(wimp_w w, char *newtitle,...) { wimp_redrawstr r; va_list ap; wimp_winfo *winfo; va_start(ap,newtitle); if((winfo = mem_alloc(sizeof(wimp_wind) + 200*sizeof(wimp_icon))) == 0) { werr(FALSE, msgs_lookup("win2:Not enough memory to change window title.")); va_end(ap); return; } tracef1("New title is %s\n", (int)newtitle); /* --- get the window's details --- */ winfo->w = w; wimpt_noerr(wimp_get_wind_info(winfo)); /* --- put the new title string in the title icon's buffer --- */ tracef1("Lib buffer is at %d\n", (int)winfo->info.title.indirecttext.buffer); vsprintf(winfo->info.title.indirecttext.buffer, newtitle, ap); va_end(ap); /* --- invalidate the title bar in absolute coords --- */ if (winfo->info.flags & wimp_WOPEN) { r.w = (wimp_w) -1; /* absolute screen coords */ r.box = winfo->info.box; r.box.y1 += 36; /* yuk - tweaky */ r.box.y0 = r.box.y1 - 36; wimpt_noerr(wimp_force_redraw(&r)); } /* --- free space used to window info --- */ mem_free(winfo); } /* * void win_gadgetWidths(wimp_wflags f,wimp_box *b) * * Use * Returns a box giving the width of the system area around a window with * window flags f. */ void win_gadgetWidths(wimp_wflags f,wimp_box *b) { wimp_wind w= { {-500,-500,-300,-300},0,0,-1, 0, {7,2,7,1,3,1,12}, {0,0,100,100}, 0, 0, (void *)1, 0, {"anywindow"}, 0 }; wimp_w temp; wimp_wstate s; wimp_redrawstr r; w.flags=f | wimp_WTRESPASS; if (wimp_create_wind(&w,&temp)) { b->x0=0; b->x1=(f & wimp_WHSCR) ? 40 : 0; b->y0=(f & wimp_WVSCR) ? 40 : 0; b->y1=(f & wimp_WTITLE) ? 40 : 0; } else { wimpt_noerr(wimp_get_wind_state(temp,&s)); wimpt_noerr(wimp_open_wind(&s.o)); wimpt_noerr(wimp_get_wind_state(temp,&s)); r.w=temp; wimpt_noerr(wimp_getwindowoutline(&r)); b->x0=s.o.box.x0-r.box.x0; b->x1=r.box.x1-s.o.box.x1; b->y1=r.box.y1-s.o.box.y1; b->y0=s.o.box.y0-r.box.y0; wimpt_noerr(wimp_delete_wind(temp)); } } /* * void win_adjustBox(wimp_openstr *o) * * Use * Adjusts a window so that it fits on the screen. * * Parameters * wimp_openstr *o == pointer to the block to fiddle */ void win_adjustBox(wimp_openstr *o) { int scx=wimpt_scwidth(); int scy=wimpt_scheight(); int w=o->box.x1-o->box.x0; int h=o->box.y1-o->box.y0; wimp_box b; wimp_wstate s; wimpt_noerr(wimp_get_wind_state(o->w,&s)); win_gadgetWidths(s.flags,&b); if (o->box.x1-o->box.x0>scx-b.x0-b.x1) { o->box.x0=b.x0; o->box.x1=scx-b.x1; } else if (o->box.x1>scx-b.x1) { o->box.x1=scx-b.x1; o->box.x0=scx-b.x1-w; } else if (o->box.x0box.x0=b.x0; o->box.x1=b.x0+w; } if (o->box.y1-o->box.y0>scy-b.y0-b.y1) { o->box.y0=b.y0; o->box.y1=scy-b.y1; } else if (o->box.y1>scy-b.y1) { o->box.y1=scy-b.y1; o->box.y0=scy-b.y1-h; } else if (o->box.y0box.y0=b.y0; o->box.y1=b.y0+h; } } /* * BOOL win_anyWindows(void) * * Use * Informs the caller if there are any windows left. * * Returns * TRUE if there are. */ BOOL win_anyWindows(void) { wimp_wind w= { {0,0,10,10},0,0,-1, 0, {7,2,7,1,3,1,12}, {0,0,10,10}, 0, 0, (void *)1, 0, {"anywindow"}, 0 }; os_error *err; wimp_w handle; err=wimp_create_wind(&w,&handle); if (err) return (FALSE); else { wimp_delete_wind(handle); return (TRUE); } } /* end */