home *** CD-ROM | disk | FTP | other *** search
-
- /* low-level.c.wimpevent
- *
- * Dreamscape - C++ class library for RISC OS
- * Copyright (c) 1996 Mark Seaborn <mseaborn@argonet.co.uk>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- * See the Dreamscape documentation for more information.
- */
-
- /*
- * This module provides a safe and efficient system for handling Wimp
- * events. It is efficient because it uses a hash table to look up handlers
- * quickly, and it is safe because handlers can be deregistered while the
- * event dispatcher is threaded - the handlers will be marked deleted and
- * removed when the dispatcher is no longer active.
- */
-
- #include <stdlib.h>
- #include "OS:wimp.h"
- #include "OS:toolbox.h"
-
- #include "wimpevent.h"
- #include "wimpmsg.h"
- #include "tboxevent.h"
- #include "task.h"
- #include "x.h"
-
-
- typedef struct handler_node handler_node;
- struct handler_node {
- short int id;
- unsigned deleted: 1;
- wimp_w window;
- void *handle;
- dscape_wimpevent_handler *function;
- handler_node *next;
- };
-
- typedef struct handler_bucket handler_bucket;
- struct handler_bucket {
- handler_node *list;
- unsigned deleted;
- };
-
- /*
- * The limit does not need to include Wimp message events as these are
- * passed directly to the wimpmsg module.
- */
- #define hash_limit 14
- static handler_bucket handlers[hash_limit], other_handlers;
- static unsigned threaded = 0, deleted = 0;
- static bool initialised = 0;
-
- static void check_initialised(void);
- static void tidy_buckets(void);
- static void tidy_bucket(handler_bucket *bucket);
-
- void dscape_wimpevent_dispatch_event(const union wimp_block *event,
- const struct toolbox_block *ids, int id)
- {
- wimp_w window = 0;
- handler_node *h;
-
- /*
- * This switch block will fill in the window variable as appropriate for
- * the Wimp message, or will go directly to another handler routine,
- * preventing the core dispatcher loop from being threaded unnecessarily.
- */
- switch(id) {
- case 0x200:
- dscape_tboxevent_dispatch_event((toolbox_action *) event, ids);
- return;
-
- case wimp_USER_MESSAGE:
- case wimp_USER_MESSAGE_RECORDED:
- case wimp_USER_MESSAGE_ACKNOWLEDGE:
- dscape_wimpmsg_dispatch_message(&event->message);
- return;
-
- case wimp_REDRAW_WINDOW_REQUEST: window = event->redraw.w; break;
- case wimp_OPEN_WINDOW_REQUEST: window = event->open.w; break;
- case wimp_CLOSE_WINDOW_REQUEST: window = event->close.w; break;
- case wimp_POINTER_LEAVING_WINDOW: window = event->leaving.w; break;
- case wimp_POINTER_ENTERING_WINDOW: window = event->entering.w; break;
- case wimp_MOUSE_CLICK: window = event->pointer.w; break;
- case wimp_KEY_PRESSED: window = event->key.w; break;
- case wimp_SCROLL_REQUEST: window = event->scroll.w; break;
-
- case wimp_LOSE_CARET:
- case wimp_GAIN_CARET: window = event->caret.w; break;
- }
-
- check_initialised();
-
- /*
- * If the event handlers for this event are in a numbered hash table
- * bucket, the loop will not bother to match the event ids.
- */
- ++threaded;
- if(id < hash_limit) {
- for(h=handlers[id].list; h; h=h->next)
- if((window == h->window || !h->window) && !h->deleted)
- if(h->function(event, ids, h->handle)) break;
- }
- else {
- for(h=other_handlers.list; h; h=h->next)
- if(id == h->id && (window == h->window || !h->window) && !h->deleted)
- if(h->function(event, ids, h->handle)) break;
- }
- --threaded;
-
- if(deleted && !threaded) tidy_buckets();
- }
-
- static void check_initialised(void)
- {
- if(!initialised) {
- int i;
- for(i=0; i<hash_limit; ++i) {
- handlers[i].list = 0;
- handlers[i].deleted = 0;
- }
- other_handlers.list = 0;
- other_handlers.deleted = 0;
- initialised = 1;
- }
- }
-
- static void tidy_buckets(void)
- {
- /*
- * This private function will tidy all the hash table buckets, removing
- * any handler nodes that have been marked as deleted.
- */
- int i;
- for(i=0; (i<hash_limit)&&deleted; ++i) tidy_bucket(&handlers[i]);
- if(deleted) tidy_bucket(&other_handlers);
- }
-
- static void tidy_bucket(handler_bucket *bucket)
- {
- /*
- * This private function will tidy a specific hash table bucket.
- */
- handler_node **d;
- for(d=&bucket->list; *d&&bucket->deleted;) {
- if((*d)->deleted) {
- handler_node *next = (*d)->next;
- free(*d);
- *d = next;
- --bucket->deleted;
- --deleted;
- }
- else {
- d = &(*d)->next;
- }
- }
- }
-
- void dscape_wimpevent_register_handler(int id, struct wimp_w_ *window,
- dscape_wimpevent_handler *function, void *handle)
- {
- /*
- * This function registers a handler, adding it to the appropriate hash
- * table bucket.
- */
- handler_node *h = malloc(sizeof(handler_node));
- if(!h) x_throw_message(x_msg_memory());
- h->id = id;
- h->window = window;
- h->function = function;
- h->handle = handle;
- h->deleted = 0;
-
- check_initialised();
- if(id < hash_limit) {
- h->next = handlers[id].list;
- handlers[id].list = h;
- }
- else {
- h->next = other_handlers.list;
- other_handlers.list = h;
- }
- }
-
- void dscape_wimpevent_deregister_handler(int id, struct wimp_w_ *window,
- dscape_wimpevent_handler *function, void *handle)
- {
- /*
- * If the dispatcher is not threaded, this function will remove the
- * handler node itself, otherwise it will simply mark the node as deleted.
- * If the node belongs in one of the numbered buckets, the function will
- * not bother matching the event ids.
- */
- if(!threaded) {
- handler_node **d;
- if(id < hash_limit) {
- for(d=&handlers[id].list; *d; d=&(*d)->next)
- if((*d)->function == function && (*d)->handle == handle &&
- (*d)->window == window && !(*d)->deleted) {
- handler_node *next = (*d)->next;
- free(*d);
- *d = next;
- return;
- }
- }
- else {
- for(d=&other_handlers.list; *d; d=&(*d)->next)
- if((*d)->function == function && (*d)->handle == handle &&
- (*d)->window == window && (*d)->id == id && !(*d)->deleted) {
- handler_node *next = (*d)->next;
- free(*d);
- *d = next;
- return;
- }
- }
- }
- else {
- handler_node *h;
- if(id < hash_limit) {
- for(h=handlers[id].list; h; h=h->next)
- if(h->function == function && h->handle == handle &&
- h->window == window && !h->deleted) {
- h->deleted = 1;
- ++handlers[id].deleted;
- ++deleted;
- return;
- }
- }
- else {
- for(h=other_handlers.list; h; h=h->next)
- if(h->function == function && h->handle == handle &&
- h->window == window && h->id == id && !h->deleted) {
- h->deleted = 1;
- ++other_handlers.deleted;
- ++deleted;
- return;
- }
- }
- }
- dscape_task_report_error(x_msg_internal("Warning: Wimp event handler "
- "was not already registered"));
- }
-