# # GFX/EVENT.D # Frame @keyFocus; Frame @mouseFocus; # INJECTNEWEVENT() # # Inject a new event into the frame hierarchy. The event will be # propogated to the appropriate frames. # public method void Frame.injectNewEvent(Event *ev) { if (ev->focusType == Event.GLOBALFOCUS && ev->type == Event.Type_ConfigureNotify) { # # Window configurations also generate expose events but # we have to clear the area due to the recalculation or # portions of the old format will still be on-screen. # #this.clearArea(ev->wpos.x, ev->wpos.y, ev->w, ev->h); } this.injectEvent(ev, this.clip.x, this.clip.y, this.clip.w, this.clip.h); } /* * INJECTEVENT() * * Inject a bounded event into the frame hierarchy. The coordinates * are relative to the current frame. */ public method void Frame.injectEvent(Event *ev, int x, int y, int w, int h) { Frame @scan; # Must adjust the box relative to the virtual (i.e. slider) position # of the sub-frame. # x -= this.vpos.x; y -= this.vpos.y; # Prevent asynchronous events from traversing frame elements # that are under construction. # if (this.validPlacement == 0) return; # In-focus events shortcut the frame hierarchy. # if (ev->focusType == Event.MOUSEFOCUS && mouseFocus != NULL) { mouseFocus->receiveEvent(ev); return; } if (ev->focusType == Event.KEYFOCUS && keyFocus != NULL) { keyFocus->receiveEvent(ev); return; } # ConfigureNotify is the primary globally focused event. It must # reclip (i.e. resize, redo) the frame hierarchy, then refresh it. # The visible portion must then be redrawn. We also get # ConfigureNotify's when we slide a window. If the width or # height does not change we ignore the event. # if (ev->focusType == Event.GLOBALFOCUS) { switch(ev->type) { case Event.Type_ConfigureNotify: # Possible initial window # Possible window resize # # Note: the bounds must be used to check for resized # windows because minp will typically be 0 # for auto-sized sub-windows. We only manually # set minp on top-level windows. # if (this.bounds.w != ev->w || this.bounds.h != ev->h ) { if (this.parent == &root) { this.minp.w = ev->w; this.minp.h = ev->h; } this.clearArea(ev->wpos.x, ev->wpos.y, ev->w, ev->h); this.recalc(); } break; } return; } # Figure out which sub-window the event belongs in. Some events, like # exposures, propogate to most or all windows. # for (scan = this.base; scan != NULL; scan = scan->next) { if (scan->clip.x + scan->pos.x <= x + w && scan->clip.x + scan->pos.x + scan->clip.w > x && scan->clip.y + scan->pos.y <= y + h && scan->clip.y + scan->pos.y + scan->clip.h > y ) { int nx; int ny; int nw; int nh; nx = scan->clip.x + scan->pos.x; ny = scan->clip.y + scan->pos.y; nw = scan->clip.w; nh = scan->clip.h; if (nx < x) { nw -= x - nx; nx = x; } if (ny < y) { nh -= y - ny; ny = y; } if (nx + nw > x + w) { nw = x + w - nx; } if (ny + nh > y + h) { nh = y + h - ny; } if (nw > 0 && nh > 0 && ev->wpos.x >= scan->wpos.x + scan->clip.x && ev->wpos.x < scan->wpos.x + scan->clip.x + scan->clip.w && ev->wpos.y >= scan->wpos.y + scan->clip.y && ev->wpos.y < scan->wpos.y + scan->clip.y + scan->clip.h ) { scan->injectEvent(ev, nx - scan->pos.x, ny - scan->pos.y, nw, nh); if (ev->focusType != Event.BROADCASTFOCUS) return; } } } this.receiveEvent(ev); } public method void Frame.receiveEvent(Event *ev) { # Calculate the frame-relative coordinates. If the event came in on a # different window we try to translate the coordinates between windows. # if (this.xid == ev->xid) { ev->pos.x = ev->wpos.x - this.wpos.x; ev->pos.y = ev->wpos.y - this.wpos.y; } else { if (this.translateWPosFrom(ev->xid, &ev->wpos)) { ev->pos.x = ev->wpos.x - this.wpos.x; ev->pos.y = ev->wpos.y - this.wpos.y; } } switch(ev->type) { case Event.Type_KeyPress: case Event.Type_KeyRelease: case Event.Type_ButtonPress: case Event.Type_ButtonRelease: this.buttonPressed(ev); break; case Event.Type_MotionNotify: this.mouseMoved(ev); break; case Event.Type_EnterNotify: this.mouseMoved(ev); break; case Event.Type_LeaveNotify: this.mouseMoved(ev); break; case Event.Type_Expose: this.refreshFrame(ev->wpos.x, ev->wpos.y, ev->w, ev->h); break; case Event.Type_MapNotify: case Event.Type_VisibilityNotify: #this.refreshVisibleFrame(); break; } } public method void Frame.setMouseFocus() { mouseFocus = &this; } public method void Frame.clearMouseFocus() { mouseFocus = NULL; } public method void Frame.setKeyFocus() { keyFocus = &this; } public method void Frame.clearKeyFocus() { keyFocus = NULL; } public method int Frame.mouseMoved(Event *ev) { # stdout->show("master mouse", ev->pos.x, ev->pos.y); } public method int Frame.buttonPressed(Event *ev) { # stdout->show("master button", ev->keySym, ev->metaMask); } public method bool Frame.mouseInBounds(Event *ev) { return(ev->pos.x >= this.clip.x && ev->pos.y >= this.clip.y && ev->pos.x < this.clip.w && ev->pos.y < this.clip.h); } public method bool Frame.mouseOutOfBounds(Event *ev) { return(ev->pos.x < this.clip.x || ev->pos.y < this.clip.y || ev->pos.x >= this.clip.w || ev->pos.y >= this.clip.h); } public method bool Frame.refreshVisibleFrame() { this.refreshFrame(this.clip.x, this.clip.y, this.clip.w, this.clip.h); } public method bool Frame.refreshFrame(int x, int y, int w, int h) { Frame @scan; for (scan = this.base; scan != NULL; scan = scan->next) { if (scan->clip.x + scan->pos.x <= x + w && scan->clip.x + scan->pos.x + scan->clip.w > x && scan->clip.y + scan->pos.y <= y + h && scan->clip.y + scan->pos.y + scan->clip.h > y ) { int nx; int ny; int nw; int nh; nx = scan->clip.x + scan->pos.x; ny = scan->clip.y + scan->pos.y; nw = scan->clip.w; nh = scan->clip.h; if (nx < x) { nw -= x - nx; nx = x; } if (ny < y) { nh -= y - ny; ny = y; } if (nx + nw > x + w) nw = x + w - nx; if (ny + nh > y + h) nh = y + h - ny; if (nw > 0 && nh > 0) scan->refreshFrame(nx - scan->pos.x, ny - scan->pos.y, nw, nh); } } } thread void Frame.eventThread() { if (++Frame.activeWindows > 1) return; result; while (Frame.activeWindows) { Event *ev = Frame.xEvent(); Frame @frame = NULL; if (ev->type == Event.Type_DestroyNotify) --Frame.activeWindows; for (frame = xRoot; frame != NULL; frame = frame->xnext) { if (ev->xid == frame->xid) { frame->injectNewEvent(ev); break; } } } }