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