home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / programming / dreamscape / source / Dreamscape / Sources / low-level / c / wimpevent < prev    next >
Encoding:
Text File  |  1996-09-03  |  6.8 KB  |  248 lines

  1.  
  2. /* low-level.c.wimpevent
  3.  *
  4.  * Dreamscape - C++ class library for RISC OS
  5.  * Copyright (c) 1996 Mark Seaborn <mseaborn@argonet.co.uk>
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Library General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2 of the License, or (at your option) any later version.
  11.  * See the Dreamscape documentation for more information.
  12.  */
  13.  
  14. /*
  15.  * This module provides a safe and efficient system for handling Wimp
  16.  * events. It is efficient because it uses a hash table to look up handlers
  17.  * quickly, and it is safe because handlers can be deregistered while the
  18.  * event dispatcher is threaded - the handlers will be marked deleted and
  19.  * removed when the dispatcher is no longer active.
  20.  */
  21.  
  22. #include <stdlib.h>
  23. #include "OS:wimp.h"
  24. #include "OS:toolbox.h"
  25.  
  26. #include "wimpevent.h"
  27. #include "wimpmsg.h"
  28. #include "tboxevent.h"
  29. #include "task.h"
  30. #include "x.h"
  31.  
  32.  
  33. typedef struct handler_node handler_node;
  34. struct handler_node {
  35.   short int id;
  36.   unsigned deleted: 1;
  37.   wimp_w window;
  38.   void *handle;
  39.   dscape_wimpevent_handler *function;
  40.   handler_node *next;
  41. };
  42.  
  43. typedef struct handler_bucket handler_bucket;
  44. struct handler_bucket {
  45.   handler_node *list;
  46.   unsigned deleted;
  47. };
  48.  
  49. /*
  50.  * The limit does not need to include Wimp message events as these are
  51.  * passed directly to the wimpmsg module.
  52.  */
  53. #define hash_limit 14
  54. static handler_bucket handlers[hash_limit], other_handlers;
  55. static unsigned threaded = 0, deleted = 0;
  56. static bool initialised = 0;
  57.  
  58. static void check_initialised(void);
  59. static void tidy_buckets(void);
  60. static void tidy_bucket(handler_bucket *bucket);
  61.  
  62. void dscape_wimpevent_dispatch_event(const union wimp_block *event,
  63.     const struct toolbox_block *ids, int id)
  64. {
  65.   wimp_w window = 0;
  66.   handler_node *h;
  67.  
  68.   /*
  69.    * This switch block will fill in the window variable as appropriate for
  70.    * the Wimp message, or will go directly to another handler routine,
  71.    * preventing the core dispatcher loop from being threaded unnecessarily.
  72.    */
  73.   switch(id) {
  74.   case 0x200:
  75.     dscape_tboxevent_dispatch_event((toolbox_action *) event, ids);
  76.     return;
  77.  
  78.   case wimp_USER_MESSAGE:
  79.   case wimp_USER_MESSAGE_RECORDED:
  80.   case wimp_USER_MESSAGE_ACKNOWLEDGE:
  81.     dscape_wimpmsg_dispatch_message(&event->message);
  82.     return;
  83.  
  84.   case wimp_REDRAW_WINDOW_REQUEST:   window = event->redraw.w; break;
  85.   case wimp_OPEN_WINDOW_REQUEST:     window = event->open.w; break;
  86.   case wimp_CLOSE_WINDOW_REQUEST:    window = event->close.w; break;
  87.   case wimp_POINTER_LEAVING_WINDOW:  window = event->leaving.w; break;
  88.   case wimp_POINTER_ENTERING_WINDOW: window = event->entering.w; break;
  89.   case wimp_MOUSE_CLICK:         window = event->pointer.w; break;
  90.   case wimp_KEY_PRESSED:         window = event->key.w; break;
  91.   case wimp_SCROLL_REQUEST:         window = event->scroll.w; break;
  92.  
  93.   case wimp_LOSE_CARET:
  94.   case wimp_GAIN_CARET:             window = event->caret.w; break;
  95.   }
  96.  
  97.   check_initialised();
  98.  
  99.   /*
  100.    * If the event handlers for this event are in a numbered hash table
  101.    * bucket, the loop will not bother to match the event ids.
  102.    */
  103.   ++threaded;
  104.   if(id < hash_limit) {
  105.     for(h=handlers[id].list; h; h=h->next)
  106.       if((window == h->window || !h->window) && !h->deleted)
  107.         if(h->function(event, ids, h->handle)) break;
  108.   }
  109.   else {
  110.     for(h=other_handlers.list; h; h=h->next)
  111.       if(id == h->id && (window == h->window || !h->window) && !h->deleted)
  112.         if(h->function(event, ids, h->handle)) break;
  113.   }
  114.   --threaded;
  115.  
  116.   if(deleted && !threaded) tidy_buckets();
  117. }
  118.  
  119. static void check_initialised(void)
  120. {
  121.   if(!initialised) {
  122.     int i;
  123.     for(i=0; i<hash_limit; ++i) {
  124.       handlers[i].list = 0;
  125.       handlers[i].deleted = 0;
  126.     }
  127.     other_handlers.list = 0;
  128.     other_handlers.deleted = 0;
  129.     initialised = 1;
  130.   }
  131. }
  132.  
  133. static void tidy_buckets(void)
  134. {
  135.   /*
  136.    * This private function will tidy all the hash table buckets, removing
  137.    * any handler nodes that have been marked as deleted.
  138.    */
  139.   int i;
  140.   for(i=0; (i<hash_limit)&&deleted; ++i) tidy_bucket(&handlers[i]);
  141.   if(deleted) tidy_bucket(&other_handlers);
  142. }
  143.  
  144. static void tidy_bucket(handler_bucket *bucket)
  145. {
  146.   /*
  147.    * This private function will tidy a specific hash table bucket.
  148.    */
  149.   handler_node **d;
  150.   for(d=&bucket->list; *d&&bucket->deleted;) {
  151.     if((*d)->deleted) {
  152.       handler_node *next = (*d)->next;
  153.       free(*d);
  154.       *d = next;
  155.       --bucket->deleted;
  156.       --deleted;
  157.     }
  158.     else {
  159.       d = &(*d)->next;
  160.     }
  161.   }
  162. }
  163.  
  164. void dscape_wimpevent_register_handler(int id, struct wimp_w_ *window,
  165.     dscape_wimpevent_handler *function, void *handle)
  166. {
  167.   /*
  168.    * This function registers a handler, adding it to the appropriate hash
  169.    * table bucket.
  170.    */
  171.   handler_node *h = malloc(sizeof(handler_node));
  172.   if(!h) x_throw_message(x_msg_memory());
  173.   h->id = id;
  174.   h->window = window;
  175.   h->function = function;
  176.   h->handle = handle;
  177.   h->deleted = 0;
  178.  
  179.   check_initialised();
  180.   if(id < hash_limit) {
  181.     h->next = handlers[id].list;
  182.     handlers[id].list = h;
  183.   }
  184.   else {
  185.     h->next = other_handlers.list;
  186.     other_handlers.list = h;
  187.   }
  188. }
  189.  
  190. void dscape_wimpevent_deregister_handler(int id, struct wimp_w_ *window,
  191.     dscape_wimpevent_handler *function, void *handle)
  192. {
  193.   /*
  194.    * If the dispatcher is not threaded, this function will remove the
  195.    * handler node itself, otherwise it will simply mark the node as deleted.
  196.    * If the node belongs in one of the numbered buckets, the function will
  197.    * not bother matching the event ids.
  198.    */
  199.   if(!threaded) {
  200.     handler_node **d;
  201.     if(id < hash_limit) {
  202.       for(d=&handlers[id].list; *d; d=&(*d)->next)
  203.           if((*d)->function == function && (*d)->handle == handle &&
  204.              (*d)->window == window && !(*d)->deleted) {
  205.         handler_node *next = (*d)->next;
  206.         free(*d);
  207.         *d = next;
  208.         return;
  209.       }
  210.     }
  211.     else {
  212.       for(d=&other_handlers.list; *d; d=&(*d)->next)
  213.           if((*d)->function == function && (*d)->handle == handle &&
  214.              (*d)->window == window && (*d)->id == id && !(*d)->deleted) {
  215.         handler_node *next = (*d)->next;
  216.         free(*d);
  217.         *d = next;
  218.         return;
  219.       }
  220.     }
  221.   }
  222.   else {
  223.     handler_node *h;
  224.     if(id < hash_limit) {
  225.       for(h=handlers[id].list; h; h=h->next)
  226.           if(h->function == function && h->handle == handle &&
  227.              h->window == window && !h->deleted) {
  228.         h->deleted = 1;
  229.         ++handlers[id].deleted;
  230.         ++deleted;
  231.         return;
  232.       }
  233.     }
  234.     else {
  235.       for(h=other_handlers.list; h; h=h->next)
  236.           if(h->function == function && h->handle == handle &&
  237.              h->window == window && h->id == id && !h->deleted) {
  238.         h->deleted = 1;
  239.         ++other_handlers.deleted;
  240.         ++deleted;
  241.         return;
  242.       }
  243.     }
  244.   }
  245.   dscape_task_report_error(x_msg_internal("Warning: Wimp event handler "
  246.     "was not already registered"));
  247. }
  248.