1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 #include <stdlib.h>
20 #include <string.h>
21 #include <SDL.h>
22 
23 #include "lg.h"
24 #include "mouse.h"
25 #include "kb.h"
26 #include "kbcook.h"
27 #include "array.h"
28 #include "rect.h"
29 #include "slab.h"
30 #include "event.h"
31 #include "vmouse.h"
32 
33 
34 // ---------------------
35 // INTERNAL PROTOTYPES
36 // ---------------------
37 void event_queue_add(uiEvent* e);
38 uchar event_queue_next(uiEvent** e);
39 uchar region_check_opacity(LGRegion* reg, ulong evmask);
40 uchar event_dispatch_callback(LGRegion* reg, LGRect* r, void* v);
41 void ui_set_last_mouse_region(LGRegion* reg, uiEvent* ev);
42 uchar ui_try_region(LGRegion* reg, LGPoint pos, uiEvent* ev);
43 uchar ui_traverse_point(LGRegion* reg, LGPoint pos, uiEvent* data);
44 uchar send_event_to_region(LGRegion* r, uiEvent* ev);
45 void ui_purge_mouse_events(void);
46 void ui_flush_mouse_events(ulong timestamp, LGPoint pos);
47 void ui_dispatch_mouse_event(uiEvent* mout);
48 void ui_poll_keyboard(void);
49 void ui_pop_up_keys(void);
50 LGPoint ui_poll_mouse_position(void);
51 errtype ui_init_focus_chain(uiSlab* slab);
52 
53 
54 // ---------------------
55 // HANDLER CHAIN DEFINES
56 // ---------------------
57 
58 typedef struct _ui_event_handler
59 {
60    ulong typemask;  // Which event types does this handle?
61      /* handler proc: called when a specific event is received */
62    uiHandlerProc proc;
63    intptr_t state;  // handler-specific state data
64    int next;     // used for chaining handlers.
65 } uiEventHandler;
66 
67 typedef struct _handler_chain
68 {
69    Array chain;
70    int front;
71    ulong opacity;
72 } handler_chain;
73 
74 
75 #define INITIAL_CHAINSIZE  4
76 #define INITIAL_FOCUSES 5
77 #define CHAIN_END -1
78 
79 ulong uiGlobalEventMask = ALL_EVENTS;
80 
81 ulong last_mouse_draw_time = 0;
82 
83 // ----------------------------
84 // HANDLER CHAIN IMPLEMENTATION
85 // ----------------------------
86 
uiInstallRegionHandler(LGRegion * r,uint32_t evmask,uiHandlerProc callback,intptr_t state,int * id)87 errtype uiInstallRegionHandler(LGRegion* r, uint32_t evmask, uiHandlerProc callback, intptr_t state, int* id)
88 {
89    handler_chain *ch;
90    uiEventHandler* eh;
91    int i;
92    errtype err;
93    // Spew(DSRC_UI_Handlers,("uiInstallRegionhandler(%x,%x,%x,%x,%x)\n",r,evmask,callback,state,id));
94    if (callback == NULL || r == NULL || evmask == 0) return ERR_NULL;
95    ch = (handler_chain*) r->handler;
96    if (ch == NULL)
97    {
98       // Spew(DSRC_UI_Handlers,("uiInstallRegionHandler(): creating new handler chain\n"));
99       ch = (handler_chain *)malloc(sizeof(handler_chain));
100       if (ch == NULL)
101       {
102          // Spew(DSRC_UI_Handlers,("uiInstallRegionHandler: out of memory\n"));
103          return ERR_NOMEM;
104       }
105       array_init(&ch->chain,sizeof(uiEventHandler),INITIAL_CHAINSIZE);
106       ch->front = CHAIN_END;
107       r->handler = (void*)ch;
108       ch->opacity = uiDefaultRegionOpacity;
109    }
110    err = array_newelem(&ch->chain,&i);
111    if (err != OK)
112    {
113       // Spew(DSRC_UI_Handlers,("uiInstallRegionHandler(): array_newelem returned %d\n",err));
114       return err;
115    }
116    eh = &((uiEventHandler*)(ch->chain.vec))[i];
117    eh->next = ch->front;
118    eh->typemask = evmask;
119    eh->proc = callback;
120    eh->state = state;
121    ch->front = i;
122    ch->opacity &= ~evmask;
123    *id = i;
124    // Spew(DSRC_UI_Handlers,("exit uiInstallRegionHandler(): *id = %d\n",*id));
125    return OK;
126 }
127 
uiRemoveRegionHandler(LGRegion * r,int id)128 errtype uiRemoveRegionHandler(LGRegion* r, int id)
129 {
130    errtype err;
131    handler_chain* ch;
132    uiEventHandler* handlers;
133    int i;
134 
135    // Spew(DSRC_UI_Handlers,("uiRemoveRegionHandler(%x,%d)\n",r,id));
136    if (r == NULL) return ERR_NULL;
137    ch = (handler_chain*)r->handler;
138    if (ch == NULL || id < 0) return ERR_RANGE;
139    handlers = (uiEventHandler*)(ch->chain.vec);
140    if (id == ch->front)
141    {
142       int next = handlers[id].next;
143       err = array_dropelem(&ch->chain,id);
144       if (err != OK) return err;
145       ch->front = next;
146       return OK;
147    }
148    for (i = ch->front; handlers[i].next != CHAIN_END; i = handlers[i].next)
149    {
150       if (handlers[i].next == id)
151       {
152          errtype err = array_dropelem(&ch->chain,id);
153          if (err != OK) return err;
154          handlers[i].next = handlers[id].next;
155          return OK;
156       }
157    }
158    return ERR_NOEFFECT;
159 }
160 
161 
uiSetRegionHandlerMask(LGRegion * r,int id,int evmask)162 errtype uiSetRegionHandlerMask(LGRegion* r, int id, int evmask)
163 {
164    handler_chain *ch;
165    uiEventHandler* handlers;
166    // Spew(DSRC_UI_Handlers,("uiSetRegionHandlerMask(%x,%d,%x)\n",r,id,evmask));
167    if (r == NULL) return ERR_NULL;
168    ch = (handler_chain*)r->handler;
169    if (ch == NULL || id >= ch->chain.fullness || id < 0) return ERR_RANGE;
170    handlers = (uiEventHandler*)(ch->chain.vec);
171    handlers[id].typemask = evmask;
172    return OK;
173 }
174 
175 // -------
176 // OPACITY
177 // -------
178 
179 ulong uiDefaultRegionOpacity = 0;
180 
uiGetRegionOpacity(LGRegion * reg)181 ulong uiGetRegionOpacity(LGRegion* reg)
182 {
183    handler_chain *ch = (handler_chain*)(reg->handler);
184    if (ch == NULL)
185    {
186       return uiDefaultRegionOpacity;
187    }
188    else
189       return ch->opacity;
190 }
191 
uiSetRegionOpacity(LGRegion * reg,ulong mask)192 errtype uiSetRegionOpacity(LGRegion* reg,ulong mask)
193 {
194    handler_chain *ch = (handler_chain*)(reg->handler);
195    if (ch == NULL)
196    {
197       // Spew(DSRC_UI_Handlers,("uiSetRegionOpacity(): creating new handler chain\n"));
198       ch = (handler_chain *)malloc(sizeof(handler_chain));
199       if (ch == NULL)
200       {
201          // Spew(DSRC_UI_Handlers,("uiSetRegionOpacity: out of memory\n"));
202          return ERR_NOMEM;
203       }
204       array_init(&ch->chain,sizeof(uiEventHandler),INITIAL_CHAINSIZE);
205       ch->front = CHAIN_END;
206       reg->handler = (void*)ch;
207       ch->opacity = mask;
208    }
209    else
210       ch->opacity = mask;
211    return OK;
212 }
213 
214 // -------------------
215 // FOCUS CHAIN DEFINES
216 // -------------------
217 
218 typedef struct _focus_link
219 {
220    LGRegion* reg;
221    ulong evmask;
222    int next;
223 } focus_link;
224 
225 extern uiSlab* uiCurrentSlab;
226 #define FocusChain (uiCurrentSlab->fchain.chain)
227 #define CurFocus (uiCurrentSlab->fchain.curfocus)
228 #define FCHAIN ((focus_link*)(uiCurrentSlab->fchain.chain.vec))
229 
230 
231 // ----------------
232 // FOCUS CHAIN CODE
233 // ----------------
234 
235 
uiGrabSlabFocus(uiSlab * slab,LGRegion * r,ulong evmask)236 errtype uiGrabSlabFocus(uiSlab* slab, LGRegion* r, ulong evmask)
237 {
238    int i;
239    errtype err;
240    focus_link* fchain = (focus_link*) slab->fchain.chain.vec;
241    // Spew(DSRC_UI_Slab,("uiGrabSlabFocus(%x,%x,%x)\n",slab,r,evmask));
242    if (r == NULL) return ERR_NULL;
243    if (evmask == 0) return ERR_NOEFFECT;
244    err = array_newelem(&slab->fchain.chain,&i);
245    if (err != OK) return err;
246    fchain[i].reg = r;
247    fchain[i].evmask = evmask;
248    fchain[i].next = slab->fchain.curfocus;
249    // Spew(DSRC_UI_Slab,("uiGrabSlabFocus(): old focus = %d new focus = %d\n",slab->fchain.curfocus,i));
250    slab->fchain.curfocus = i;
251    return OK;
252 }
253 
254 
uiGrabFocus(LGRegion * r,ulong evmask)255 errtype uiGrabFocus(LGRegion* r, ulong evmask)
256 {
257    return uiGrabSlabFocus(uiCurrentSlab,r,evmask);
258 }
259 
uiReleaseSlabFocus(uiSlab * slab,LGRegion * r,ulong evmask)260 errtype uiReleaseSlabFocus(uiSlab* slab, LGRegion* r, ulong evmask)
261 {
262    errtype retval = ERR_NOEFFECT;
263    focus_link* fchain = (focus_link*)slab->fchain.chain.vec;
264    focus_link *l = &fchain[CurFocus];
265    // Spew(DSRC_UI_Slab,("uiReleaseSlabFocus(%x,%x,%x)\n",slab,r,evmask));
266    if (r == NULL) return ERR_NULL;
267    if (l->reg == r)
268    {
269       ulong tmpmask = l->evmask & evmask;
270       l->evmask &= ~evmask;
271       evmask &= ~tmpmask;
272       if (l->evmask == 0)
273       {
274          int tmp = slab->fchain.curfocus;
275          slab->fchain.curfocus = l->next;
276          // Spew(DSRC_UI_Slab,("uiReleaseSlabFocus(): CurFocus = %d\n",slab->fchain.curfocus));
277          array_dropelem(&slab->fchain.chain,tmp);
278       }
279       if (evmask == 0) return OK;
280       retval = OK;
281 
282    }
283    for(; l->next != CHAIN_END; l = &fchain[l->next])
284    {
285       focus_link* thenext = &fchain[l->next];
286       if (thenext->reg == r)
287       {
288          ulong tmpmask = l->evmask & evmask;
289          thenext->evmask &= ~evmask;
290          evmask &= ~tmpmask;
291          if (thenext->evmask == 0)
292          {
293             int tmp = l->next;
294             l->next = thenext->next;
295             array_dropelem(&slab->fchain.chain,tmp);
296          }
297          if (evmask == 0)
298             return OK;
299          retval = OK;
300       }
301    }
302    return retval;
303 }
304 
uiReleaseFocus(LGRegion * r,ulong evmask)305 errtype uiReleaseFocus(LGRegion* r, ulong evmask)
306 {
307    return uiReleaseSlabFocus(uiCurrentSlab,r,evmask);
308 }
309 
310 
311 // -----------------------
312 // POLLING AND DISPATCHING
313 // -----------------------
314 
315 #define INITIAL_QUEUE_SIZE 32
316 #define DEFAULT_DBLCLICKTIME  0
317 #define DEFAULT_DBLCLICKDELAY 0
318 
319 ushort uiDoubleClickTime = DEFAULT_DBLCLICKTIME;
320 ushort uiDoubleClickDelay = DEFAULT_DBLCLICKDELAY;
321 uchar   uiDoubleClicksOn[NUM_MOUSE_BTNS] = { FALSE, FALSE, FALSE } ;
322 uchar   uiAltDoubleClick = FALSE;
323 ushort uiDoubleClickTolerance = 5;
324 static uchar   poll_mouse_motion = FALSE;
325 static uiEvent last_down_events[NUM_MOUSE_BTNS];
326 static uiEvent last_up_events[NUM_MOUSE_BTNS];
327 
328 static struct _eventqueue
329 {
330    int in, out;
331    int size;
332    uiEvent* vec;
333 } EventQueue;
334 
event_queue_add(uiEvent * e)335 void event_queue_add(uiEvent* e)
336 {
337    if ((EventQueue.in + 1)%EventQueue.size == EventQueue.out)
338    {
339       // Queue is full, grow it.
340       int i;
341       int out = EventQueue.out;
342       int newsize = EventQueue.size * 2;
343       uiEvent *newvec = (uiEvent *)malloc(sizeof(uiEvent)*newsize);
344       for(i = 0; out != EventQueue.in; i++, out = (out+1)%EventQueue.size)
345          newvec[i] = EventQueue.vec[out];
346       free(EventQueue.vec);
347       EventQueue.vec = newvec;
348       EventQueue.size = newsize;
349       EventQueue.in = i;
350       EventQueue.out = 0;
351    }
352    EventQueue.vec[EventQueue.in] = *e;
353    EventQueue.in++;
354    if (EventQueue.in >= EventQueue.size) EventQueue.in = 0;
355 }
356 
event_queue_next(uiEvent ** e)357 uchar event_queue_next(uiEvent** e)
358 {
359    if (EventQueue.in != EventQueue.out)
360    {
361       *e = &EventQueue.vec[EventQueue.out++];
362       if (EventQueue.out >= EventQueue.size)
363          EventQueue.out = 0;
364       return TRUE;
365    }
366    return FALSE;
367 }
368 
369 
370 // TRUE we are opaque to this mask.
region_check_opacity(LGRegion * reg,ulong evmask)371 uchar region_check_opacity(LGRegion* reg, ulong evmask)
372 {
373    return (evmask & uiGetRegionOpacity(reg)) != 0;
374 }
375 
event_dispatch_callback(LGRegion * reg,LGRect * rect,void * v)376 uchar event_dispatch_callback(LGRegion* reg, LGRect* rect, void* v)
377 {
378    uiEvent* ev = (uiEvent*)v;
379    handler_chain *ch = (handler_chain*)(reg->handler);
380    int i,next;
381 
382    if(ch == NULL) {
383       //printf("WARNING! handler_chain is NULL in event_dispatch_callback\n");
384       return FALSE;
385    }
386 
387    uiEventHandler* handlers = (uiEventHandler*)(ch->chain.vec);
388    // Spew(DSRC_UI_Dispatch,("event_dispatch_callback(%x,%x,%x) event type %x\n",reg,r,v,ev->type));
389 /*
390 if (ev->type == UI_EVENT_KBD_COOKED)
391 {
392 	char buff[100];
393 	sprintf(buff+1, "event_dispatch_callback(%x,%x,%x) event type %x\0",reg,r,v,ev->type);
394 	buff[0] = strlen(buff+1);
395 	DebugString((uchar *)buff);
396 }
397 */
398    if (ch == NULL || handlers ==  NULL)
399    {
400       // Spew(DSRC_UI_Dispatch,("event_dispatch_callback(): no handler chain ch = %x handlers = %d\n",ch,handlers));
401       return FALSE;
402    }
403    for (i = ch->front; i != CHAIN_END; i = next)
404    {
405       next = handlers[i].next;
406       if ((handlers[i].typemask & ev->type)
407             && (handlers[i].proc)(ev,reg,handlers[i].state))
408       {
409          // Spew(DSRC_UI_Dispatch,("Caught by handler %d\n",i));
410          return TRUE;
411       }
412    }
413    // Spew(DSRC_UI_Dispatch,("Event Rejected\n"));
414    return FALSE;
415 }
416 
417 // ui_traverse_point return values:
418 #define TRAVERSE_HIT 0
419 #define TRAVERSE_MISS 1
420 #define TRAVERSE_OPAQUE 2
421 
422 LGRegion* uiLastMouseRegion[NUM_MOUSE_BTNS];
423 
424 
ui_set_last_mouse_region(LGRegion * reg,uiEvent * ev)425 void ui_set_last_mouse_region(LGRegion* reg, uiEvent* ev)
426 {
427    int i;
428    if (ev->type != UI_EVENT_MOUSE)
429       return;
430    for (i = 0; i < NUM_MOUSE_BTNS; i++)
431    {
432       if ((ev->mouse_data.action & MOUSE_BTN2DOWN(i)) != 0 ||
433           (ev->mouse_data.action & UI_MOUSE_BTN2DOUBLE(i)))
434             uiLastMouseRegion[i] = reg;
435       if (ev->mouse_data.action & MOUSE_BTN2UP(i))
436          uiLastMouseRegion[i] = NULL;
437    }
438 }
439 
ui_try_region(LGRegion * reg,LGPoint pos,uiEvent * ev)440 uchar ui_try_region(LGRegion* reg, LGPoint pos, uiEvent* ev)
441 {
442    LGRect cbr;
443    uchar retval = TRAVERSE_MISS;
444 
445    cbr.ul = pos;
446    cbr.lr = pos;
447    if (region_check_opacity(reg,ev->type)) retval = TRAVERSE_OPAQUE;
448    else if (event_dispatch_callback(reg,&cbr,ev)) retval = TRAVERSE_HIT;
449    else return retval;
450    ui_set_last_mouse_region(reg, ev);
451    return retval;
452 }
453 
ui_traverse_point(LGRegion * reg,LGPoint pos,uiEvent * data)454 uchar ui_traverse_point(LGRegion* reg, LGPoint pos, uiEvent* data)
455 {
456    uchar retval = TRAVERSE_MISS;
457    LGPoint rel;
458    LGRegion* child;
459 
460    rel = pos;
461    rel.x -= reg->abs_x;
462    rel.y -= reg->abs_y;
463 
464    if ((reg->status_flags & INVISIBLE_FLAG) != 0)
465       return retval;
466 
467    if (reg->event_order)
468    {
469       retval = ui_try_region(reg,pos,data);
470       if (retval != TRAVERSE_MISS) return retval;
471    }
472    for (child = reg->sub_region; child != NULL; child = child->next_region)
473       if (RECT_TEST_PT(child->r,rel))
474       {
475          retval = ui_traverse_point(child,pos,data);
476          if (retval != TRAVERSE_MISS) return retval;
477          break;
478       }
479    if (!reg->event_order)
480    {
481       retval = ui_try_region(reg,pos,data);
482       if (retval != TRAVERSE_MISS) return retval;
483    }
484    return TRAVERSE_MISS;
485 }
486 
send_event_to_region(LGRegion * r,uiEvent * ev)487 uchar send_event_to_region(LGRegion* r, uiEvent* ev)
488 {
489    // Spew(DSRC_UI_Dispatch,("send_event_to_region(%x,%x)\n",r,ev));
490    return ui_traverse_point(r,ev->pos,ev) == TRAVERSE_HIT;
491 }
492 
uiDispatchEventToRegion(uiEvent * ev,LGRegion * reg)493 uchar uiDispatchEventToRegion(uiEvent* ev, LGRegion* reg)
494 {
495    LGPoint pos;
496    uiEvent nev = *ev;
497 
498    ui_mouse_do_conversion(&(nev.pos.x),&(nev.pos.y),TRUE);
499    pos = nev.pos;
500    pos.x += reg->r->ul.x - reg->abs_x;
501    pos.y += reg->r->ul.y - reg->abs_y;
502 
503    if (!RECT_TEST_PT(reg->r,pos))
504    {
505       LGRect r;
506       r.ul = nev.pos;
507       r.lr.x = nev.pos.x+1;
508       r.lr.y = nev.pos.y+1;
509       return event_dispatch_callback(reg,&r,&nev);
510    }
511    return ui_traverse_point(reg,nev.pos,&nev) == TRAVERSE_HIT;
512 }
513 
514 
uiDispatchEvent(uiEvent * ev)515 uchar uiDispatchEvent(uiEvent* ev)
516 {
517    int i;
518    // Spew(DSRC_UI_Dispatch,("dispatch_event(%x), CurFocus = %d\n",ev,CurFocus));
519    if (!(ev->type & uiGlobalEventMask)) return FALSE;
520    for (i = CurFocus; i != CHAIN_END; i = FCHAIN[i].next)
521    {
522       // Spew(DSRC_UI_Dispatch,("dispatch_event(): checking focus chain element %d\n",i));
523       if (FCHAIN[i].evmask & ev->type)
524          if (uiDispatchEventToRegion(ev,FCHAIN[i].reg)) return TRUE;
525    }
526    return FALSE;
527 }
528 
uiQueueEvent(uiEvent * ev)529 errtype uiQueueEvent(uiEvent* ev)
530 {
531    // if this is a keyboard event, queue up earlier events.
532    if (ev->type == UI_EVENT_KBD_RAW || ev->type == UI_EVENT_KBD_COOKED)
533    {
534       kbs_event kbe;
535       for(kbe = kb_next(); kbe.code != KBC_NONE; kbe = kb_next())
536       {
537          uiEvent out;
538          mouse_get_xy(&out.pos.x,&out.pos.y);
539          out.type = UI_EVENT_KBD_RAW;
540          out.raw_key_data.scancode = kbe.code;
541          out.raw_key_data.action = kbe.state;
542          event_queue_add(&out);
543       }
544    }
545    if (ev->type == UI_EVENT_MOUSE || ev->type == UI_EVENT_MOUSE_MOVE)
546    {
547       ss_mouse_event mse;
548       errtype err = mouse_next(&mse);
549       for(;err == OK; err = mouse_next(&mse))
550       {
551          uiEvent out;
552          out.pos.x = mse.x;
553          out.pos.y = mse.y;
554          out.type = (mse.type == MOUSE_MOTION) ? UI_EVENT_MOUSE_MOVE :  UI_EVENT_MOUSE;
555          out.mouse_data.action = mse.type;
556          out.mouse_data.modifiers = mse.modifiers;
557          event_queue_add((uiEvent*)&out);
558       }
559    }
560    event_queue_add(ev);
561    return OK;
562 }
563 
564 #define MOUSE_EVENT_FLUSHED UI_EVENT_MOUSE_MOVE
565 
ui_purge_mouse_events(void)566 void ui_purge_mouse_events(void)
567 {
568    int i;
569    for (i = 0; i < NUM_MOUSE_BTNS; i++)
570    {
571       last_down_events[i].type = UI_EVENT_NULL;
572       last_down_events[i].mouse_data.tstamp = 0;
573       last_up_events[i].type = UI_EVENT_NULL;
574       last_up_events[i].mouse_data.tstamp = 0;
575    }
576 }
577 
ui_flush_mouse_events(ulong timestamp,LGPoint pos)578 void ui_flush_mouse_events(ulong timestamp, LGPoint pos)
579 {
580    int i;
581    for (i = 0; i < NUM_MOUSE_BTNS; i++)
582    {
583 
584       if (uiDoubleClicksOn[i] &&
585          last_down_events[i].type != UI_EVENT_NULL)
586       {
587          int crit = uiDoubleClickDelay * 5;
588          ulong timediff = timestamp - last_down_events[i].mouse_data.tstamp;
589          LGPoint downpos = last_down_events[i].pos;
590          uchar out = (abs(pos.x - downpos.x) > uiDoubleClickTolerance ||
591                      abs(pos.y - downpos.y) > uiDoubleClickTolerance);
592 
593          // OK, if we've waited DoubleClickDelay after a down event, send it out.
594          if (out || timediff >= crit)
595          {
596             uiEvent ev;
597             //Spew(DSRC_UI_Polling,("flushing old clicks: crit = %d timediff = %d\n",crit,timediff));
598             if (last_down_events[i].type != MOUSE_EVENT_FLUSHED)
599             {
600                ev = last_down_events[i];
601                last_down_events[i].type = MOUSE_EVENT_FLUSHED;
602                uiDispatchEvent(&ev);
603             }
604             if (last_up_events[i].type != MOUSE_EVENT_FLUSHED)
605             {
606                ev = last_up_events[i];
607                last_up_events[i].type = MOUSE_EVENT_FLUSHED;
608                uiDispatchEvent(&ev);
609             }
610          }
611 
612          // This is where we do our flushing
613          if (last_up_events[i].type != UI_EVENT_NULL)
614          {
615             crit = uiDoubleClickTime;
616             timediff = timestamp - last_up_events[i].mouse_data.tstamp;
617             if (out || timediff >= crit)
618             {
619                last_down_events[i].type   = UI_EVENT_NULL;
620                  last_up_events[i].type   = UI_EVENT_NULL;
621             }
622          }
623       }
624    }
625 }
626 
ui_dispatch_mouse_event(uiEvent * mout)627 void ui_dispatch_mouse_event(uiEvent* mout)
628 {
629    int i;
630    uchar eaten = FALSE;
631 
632    bool altDown = (SDL_GetModState() & KMOD_ALT) != 0;
633 
634 //   ui_mouse_do_conversion(&(mout->pos.x),&(mout->pos.y),TRUE);
635    ui_flush_mouse_events(mout->mouse_data.tstamp, mout->pos);
636    for (i = 0; i < NUM_MOUSE_BTNS; i++)
637    {
638       if (!(uiDoubleClicksOn[i]))
639          continue;
640 
641       //printf("Checking double click! %i\n", mout->action & MOUSE_BTN2DOWN(i));
642       if (uiAltDoubleClick && altDown)
643       {
644          if (mout->mouse_data.action & MOUSE_BTN2DOWN(i))
645          {
646             mout->mouse_data.action &= ~MOUSE_BTN2DOWN(i);
647             mout->mouse_data.action |= UI_MOUSE_BTN2DOUBLE(i);
648             continue;
649          }
650       }
651       if (last_down_events[i].type != UI_EVENT_NULL)
652       {
653          if (mout->mouse_data.action & MOUSE_BTN2DOWN(i))
654          {
655             // Spew(DSRC_UI_Polling,("double click down\n"));
656             // make a double click event.
657             mout->mouse_data.action &= ~MOUSE_BTN2DOWN(i);
658             mout->mouse_data.action |= UI_MOUSE_BTN2DOUBLE(i);
659 
660             last_down_events[i].type = UI_EVENT_NULL;
661             last_up_events[i].type = UI_EVENT_NULL;
662          }
663          if (mout->mouse_data.action & MOUSE_BTN2UP(i))
664          {
665             // Spew(DSRC_UI_Polling,("up in time %d\n",mout->tstamp - last_down_events[i].tstamp));
666             last_up_events[i] = *mout;
667             eaten = TRUE;
668          }
669       }
670       else if (mout->mouse_data.action & MOUSE_BTN2DOWN(i))
671       {
672          // Spew(DSRC_UI_Polling,("saving the down\n"));
673          last_down_events[i] = *mout;
674          eaten = TRUE;
675       }
676    }
677    if (!eaten)
678       uiDispatchEvent(mout);
679 }
680 
681 // ----------------------
682 // KEYBOARD POLLING SETUP
683 // ----------------------
684 
685 uchar* ui_poll_keys = NULL;
686 
uiSetKeyboardPolling(ubyte * codes)687 errtype uiSetKeyboardPolling(ubyte* codes)
688 {
689    ui_poll_keys = codes;
690    return OK;
691 }
692 
693 extern uchar sshockKeyStates[256];
694 
inputModToUImod(uchar mod)695 static ushort inputModToUImod(uchar mod)
696 {
697 	ushort ret = 0;
698 	if(mod & KB_MOD_CTRL)
699 		ret |= KB_FLAG_CTRL;
700 	if(mod & KB_MOD_SHIFT)
701 		ret |= KB_FLAG_SHIFT;
702 	// TODO: what's 0x04 ? windows key?
703 	if(mod & KB_MOD_ALT)
704 		ret |= KB_FLAG_ALT;
705 	// Note: KB_MOD_PRESSED doesn't matter here
706 
707 	return ret;
708 }
709 
710 // KLC - For Mac version, call GetKeys once at the beginning, then check
711 // the results in the loop.  Fill in the "mods" field (ready for cooking)
712 // before dispatching an event.
ui_poll_keyboard(void)713 void ui_poll_keyboard(void)
714 {
715 	int numKeys = 0;
716 
717 	for(uchar* key = ui_poll_keys; *key != KBC_NONE; key++)
718 	{
719 		if(sshockKeyStates[*key] != 0)
720 		{
721 			uiEvent ev;
722 			ev.type = UI_EVENT_KBD_POLL;
723 			ev.pos.x = 0;
724 			ev.pos.y = 0;
725 			ev.poll_key_data.action = KBS_DOWN;
726 			ev.poll_key_data.scancode = *key;
727 			ev.poll_key_data.mods = inputModToUImod(sshockKeyStates[*key]);
728 
729 			uiDispatchEvent(&ev);
730 		}
731 		// *key is a System Shock/Mac keycode
732 	}
733 
734 #if 0
735 	extern uchar	pKbdGetKeys[16];
736 	long			*keys = (long *)pKbdGetKeys;
737 	GetKeys((UInt32 *)keys);
738 
739 	uchar *key;
740 	for (key = ui_poll_keys; *key != KBC_NONE; key++)
741 		if((pKbdGetKeys[*key>>3] >> (*key & 7)) & 1)
742 		{
743 			uiPollKeyEvent ev;
744 			ev.type = UI_EVENT_KBD_POLL;
745 			ev.pos.x = 0;
746 			ev.pos.y = 0;
747 			ev.action = KBS_DOWN;
748 			ev.scancode = *key;
749 			ev.mods = 0;
750 			if ((keys[1] & 0x00000001) != 0L)	// Shift key
751 				ev.mods |= KB_FLAG_SHIFT;
752 			if ((keys[1] & 0x00008000) != 0L)	// Cmd key
753 				ev.mods |= KB_FLAG_CTRL;
754 			if ((keys[1] & 0x00000004) != 0L)	// Option key
755 				ev.mods |= KB_FLAG_ALT;
756 
757 			uiDispatchEvent((uiEvent*)&ev);
758 		}
759 #endif
760 }
761 
ui_pop_up_keys(void)762 void ui_pop_up_keys(void)
763 {
764 /*¥¥¥ serve any purpose now?
765    if (ui_poll_keys != NULL)
766    {
767       uchar* key;
768       for (key = ui_poll_keys; *key != KBC_NONE; key++)
769       {
770          kb_clear_state(*key,KBA_STATE);
771       }
772    }
773 */
774 }
775 
uiMakeMotionEvent(uiEvent * ev)776 errtype uiMakeMotionEvent(uiEvent* ev)
777 {
778    // haha, this is the super secret mouse library variable of the
779    // current button state.
780    extern short mouseInstantButts;
781    mouse_get_xy(&ev->pos.x,&ev->pos.y);
782    ev->type = UI_EVENT_MOUSE_MOVE; // must get past event mask
783    ev->mouse_data.action = MOUSE_MOTION;
784    ev->mouse_data.tstamp = mouse_get_time();
785    ev->mouse_data.buttons = (ubyte)mouseInstantButts;
786    return OK;
787 }
788 
789 // Generate a fake mouse motion event and send it along...
ui_poll_mouse_position(void)790 LGPoint ui_poll_mouse_position(void)
791 {
792    uiEvent ev;
793    uiMakeMotionEvent(&ev);
794    uiDispatchEvent(&ev);
795    return ev.pos;
796 }
797 
798 
uiPoll(void)799 errtype uiPoll(void)
800 {
801    static LGPoint last_mouse = { -1, -1 };
802    errtype err;
803    uiEvent out,*ev;
804    uchar kbdone = FALSE;
805    uchar msdone = FALSE;
806    LGPoint mousepos = last_mouse;
807    extern LGPoint LastCursorPos;
808    extern struct _cursor* LastCursor;
809    extern void ui_update_cursor(LGPoint pos);
810 
811 #define BURN_QUEUE
812 #ifdef BURN_QUEUE
813    // burn through queue
814    while(event_queue_next(&ev))
815    {
816       uchar result = TRUE;
817 //      ui_mouse_do_conversion(&(ev->pos.x),&(ev->pos.y),TRUE);
818       if (ev->type == UI_EVENT_MOUSE)
819          ui_dispatch_mouse_event(ev);
820       else result = uiDispatchEvent(ev);
821       if (!result && ev->type == UI_EVENT_KBD_RAW)
822       {
823          ushort cooked;
824          kbs_event kbe;
825          kbe.code = ev->raw_key_data.scancode;
826          kbe.state = ev->raw_key_data.action;
827          err = kb_cook(kbe,&cooked,&result);
828          if (err != OK) return err;
829          if (result)
830          {
831             out.subtype = cooked;
832             out.type = UI_EVENT_KBD_COOKED;
833             uiDispatchEvent(ev);
834          }
835       }
836    }
837 #endif // BURN_QUEUE
838 
839 //   ui_mouse_get_xy(&mousepos.x,&mousepos.y);
840 
841    mouse_get_xy(&mousepos.x,&mousepos.y);
842 
843    while(!kbdone || !msdone)
844    {
845       if (!kbdone)
846       {
847          kbs_event kbe = kb_next();
848          if (kbe.code != KBC_NONE)
849          {
850             uchar eaten;
851             // Spew(DSRC_UI_Polling,("uiPoll(): got a keyboard event: <%d,%x>\n",kbe.state,kbe.code));
852             out.pos = mousepos;
853             out.type = UI_EVENT_KBD_RAW;
854             out.raw_key_data.scancode = kbe.code;
855             out.raw_key_data.action = kbe.state;
856             eaten = uiDispatchEvent(&out);
857             if (!eaten)
858             {
859               ushort cooked;
860               uchar result;
861               // Spew(DSRC_UI_Polling,("uiPoll(): cooking keyboard event: <%d,%x>\n",kbe.state,kbe.code));
862               err = kb_cook(kbe,&cooked,&result);
863               if (err != OK) return err;
864               if (result)
865               {
866                out.subtype = cooked;
867                out.type = UI_EVENT_KBD_COOKED;
868                eaten = uiDispatchEvent(&out);
869               }
870             }
871 //            if (eaten)
872 //            {
873 //               kb_clear_state(kbe.code,KBA_STATE);
874 //            }
875          }
876          else kbdone = TRUE;
877       }
878       if (!msdone)
879       {
880          ss_mouse_event mse;
881          errtype err = mouse_next(&mse);
882          /*if (poll_mouse_motion)
883             while (mse.type == MOUSE_MOTION  && err == OK)
884             {
885                err = mouse_next(&mse);
886             }*/
887 
888          if (err == OK)
889          {
890             out.pos.x = mse.x;
891             out.pos.y = mse.y;
892             // note that the equality operator here means that motion-only
893             // events are MOUSE_MOVE, and others are MOUSE events.
894             out.type = (mse.type == MOUSE_MOTION) ? UI_EVENT_MOUSE_MOVE :  UI_EVENT_MOUSE;
895             out.subtype = mse.type;
896             out.mouse_data.tstamp = mse.timestamp;
897             out.mouse_data.buttons = mse.buttons;
898 	    out.mouse_data.modifiers = mse.modifiers;
899             ui_dispatch_mouse_event(&out);
900 //            uiDispatchEvent((uiEvent*)mout);
901          }
902          else msdone = TRUE;
903       }
904    }
905 
906    if (poll_mouse_motion)
907    {
908       mousepos = ui_poll_mouse_position();
909    }
910    if (ui_poll_keys != NULL && (uiGlobalEventMask & UI_EVENT_KBD_POLL))
911       ui_poll_keyboard();
912    ui_flush_mouse_events(mouse_get_time(),mousepos);
913 
914    // CC: Make sure the attack cursor doesn't display forever!
915    int diff = mouse_get_time() - last_mouse_draw_time;
916 
917    if (!PointsEqual(mousepos,last_mouse) || diff > uiDoubleClickDelay * 5)
918    {
919       ui_update_cursor(mousepos);
920       last_mouse = mousepos;
921       last_mouse_draw_time = mouse_get_time();
922    }
923 
924    return OK;
925 }
926 
uiSetMouseMotionPolling(uchar poll)927 errtype uiSetMouseMotionPolling(uchar poll)
928 {
929    if (poll) mouseMask &= ~MOUSE_MOTION;
930    else mouseMask |= MOUSE_MOTION;
931    poll_mouse_motion = poll;
932    return OK;
933 }
934 
935 
936 
uiFlush(void)937 errtype uiFlush(void)
938 {
939    uiEvent* e;
940    kbs_event kbe = kb_next();
941    mouse_flush();
942 
943    while (kbe.code != KBC_NONE)
944    {
945       ushort dummy;
946       uchar result;
947       kb_cook(kbe,&dummy,&result);
948       kbe = kb_next();
949    }
950    while(event_queue_next(&e));
951    ui_pop_up_keys();
952    ui_purge_mouse_events();
953    return OK;
954 }
955 
uiCheckInput(void)956 uchar uiCheckInput(void)
957 {
958    kbs_event kbe;
959    ss_mouse_event mse;
960    kbe = kb_next();
961    if (kbe.code != KBC_NONE)
962    {
963       ushort cooked;
964       uchar res;
965       kb_cook(kbe,&cooked,&res);
966       if (kbe.state == KBS_DOWN)
967       {
968          ui_pop_up_keys();
969          return TRUE;
970       }
971    }
972    if (mouse_next(&mse) == OK)
973    {
974       int i;
975       for (i = 0; i < NUM_MOUSE_BTNS; i++)
976       {
977          if ((mse.type & MOUSE_BTN2DOWN(i) ) != 0)
978             return TRUE;
979       }
980    }
981    return FALSE;
982 }
983 
984 // ---------------------------
985 // INITIALIZATION AND SHUTDOWN
986 // ---------------------------
987 
988 //char keybuf[512];
989 
uiInit(uiSlab * slab)990 errtype uiInit(uiSlab* slab)
991 {
992    int i;
993    errtype err;
994 
995    uiSetCurrentSlab(slab);
996 //KLC - moved to main program   mouse_init(grd_cap->w,grd_cap->h);
997 //KLC - moved to main program   kb_init(NULL);
998    // initialize the event queue;
999    EventQueue.in = EventQueue.out = 0;
1000    EventQueue.size = INITIAL_QUEUE_SIZE;
1001    EventQueue.vec = (uiEvent *)malloc(sizeof(uiEvent)*INITIAL_QUEUE_SIZE);
1002    for (i = 0; i < NUM_MOUSE_BTNS; i++)
1003       last_down_events[i].type = UI_EVENT_NULL;
1004 //KLC - done in main program now.   err = ui_init_cursors();
1005    if (err != OK) return err;
1006 //KLC -   AtExit(uiShutdown);
1007    return OK;
1008 }
1009 
uiShutdown(void)1010 void uiShutdown(void)
1011 {
1012    extern errtype ui_shutdown_cursors(void);
1013    ui_shutdown_cursors();
1014    mouse_shutdown();
1015    kb_close();
1016 }
1017 
1018 
uiShutdownRegionHandlers(LGRegion * r)1019 errtype uiShutdownRegionHandlers(LGRegion* r)
1020 {
1021    errtype err = OK;
1022    handler_chain *ch = (handler_chain*)(r->handler);
1023    if (ch == NULL) return ERR_NOEFFECT;
1024    err = array_destroy(&ch->chain);
1025    free(ch);
1026    return err;
1027 }
1028 
ui_init_focus_chain(uiSlab * slab)1029 errtype ui_init_focus_chain(uiSlab* slab)
1030 {
1031    errtype err = array_init(&slab->fchain.chain,sizeof(focus_link),INITIAL_FOCUSES);
1032    if (err != OK)  return err;
1033    slab->fchain.curfocus = CHAIN_END;
1034    return OK;
1035 }
1036 
1037 
1038 
1039 
1040 
1041 
1042 
1043 
1044 
1045 
1046 
1047 
1048 
1049