Blender 2.5 project: added first more complex handler + operator
[blender.git] / source / blender / windowmanager / intern / wm_event_system.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2007 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "DNA_listBase.h"
33 #include "DNA_screen_types.h"
34 #include "DNA_windowmanager_types.h"
35 #include "DNA_userdef_types.h"  /* U.flag & TWOBUTTONMOUSE */
36
37 #include "MEM_guardedalloc.h"
38
39 #include "GHOST_C-api.h"
40
41 #include "BLI_blenlib.h"
42
43 #include "BKE_blender.h"
44 #include "BKE_global.h"
45
46 #include "ED_screen.h"
47 #include "ED_area.h"
48
49 #include "WM_api.h"
50 #include "WM_types.h"
51 #include "wm.h"
52 #include "wm_window.h"
53 #include "wm_event_system.h"
54 #include "wm_event_types.h"
55
56 /* ************ event management ************** */
57
58 static void wm_event_add(wmWindow *win, wmEvent *event_to_add)
59 {
60         wmEvent *event= MEM_callocN(sizeof(wmEvent), "event");
61         
62         *event= *event_to_add;
63         BLI_addtail(&win->queue, event);
64 }
65
66 wmEvent *wm_event_next(wmWindow *win)
67 {
68         wmEvent *event= win->queue.first;
69
70         if(event) BLI_remlink(&win->queue, event);
71         return event;
72 }
73
74 static void wm_event_free(wmEvent *event)
75 {
76         if(event->customdata) MEM_freeN(event->customdata);
77         MEM_freeN(event);
78 }
79
80 void wm_event_free_all(wmWindow *win)
81 {
82         wmEvent *event;
83         
84         while((event= win->queue.first)) {
85                 BLI_remlink(&win->queue, event);
86                 wm_event_free(event);
87         }
88 }
89
90 /* ********************* notifiers, listeners *************** */
91
92 /* win and swinid are optional context limitors */
93 void WM_event_add_notifier(wmWindowManager *wm, wmWindow *window, int swinid, int type, int value)
94 {
95         wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
96         
97         BLI_addtail(&wm->queue, note);
98         
99         note->window= window;
100         note->swinid= swinid;
101         note->type= type;
102         note->value= value;
103 }
104
105 static wmNotifier *wm_notifier_next(wmWindowManager *wm)
106 {
107         wmNotifier *note= wm->queue.first;
108         
109         if(note) BLI_remlink(&wm->queue, note);
110         return note;
111 }
112
113 /* called in mainloop */
114 void wm_event_do_notifiers(bContext *C)
115 {
116         wmNotifier *note;
117         
118         while( (note=wm_notifier_next(C->wm)) ) {
119                 wmWindow *win;
120                 
121                 for(win= C->wm->windows.first; win; win= win->next) {
122                         ScrArea *sa;
123                         
124                         if(note->window && note->window!=win)
125                                 continue;
126                         if(win->screen==NULL)
127                                 continue;
128                         printf("notifier win %d screen %s\n", win->winid, win->screen->id.name+2);
129                         ED_screen_do_listen(win->screen, note);
130                         
131                         for(sa= win->screen->areabase.first; sa; sa= sa->next) {
132                                 ARegion *ar= sa->regionbase.first;
133                                 
134                                 for(; ar; ar= ar->next) {
135                                         if(note->swinid && note->swinid!=ar->swinid)
136                                                 continue;
137                                         ED_region_do_listen(ar, note);
138                                 }
139                         }
140                 }
141                 MEM_freeN(note);
142         }       
143 }
144
145 /* quick test to prevent changing window drawable */
146 static int wm_draw_update_test_window(wmWindow *win)
147 {
148         ScrArea *sa;
149         
150         if(win->screen->do_refresh)
151                 return 1;
152         if(win->screen->do_draw)
153                 return 1;
154         
155         for(sa= win->screen->areabase.first; sa; sa= sa->next) {
156                 ARegion *ar= sa->regionbase.first;
157                 
158                 for(; ar; ar= ar->next) {
159                         /* cached notifiers */
160                         if(ar->do_refresh)
161                                 return 1;
162                         if(ar->swinid && ar->do_draw)
163                                 return 1;
164                 }
165         }
166         return 0;
167 }
168
169 void wm_draw_update(bContext *C)
170 {
171         wmWindow *win;
172         
173         for(win= C->wm->windows.first; win; win= win->next) {
174                 if(wm_draw_update_test_window(win)) {
175                         ScrArea *sa;
176                         
177                         /* sets context window+screen */
178                         wm_window_make_drawable(C, win);
179                         
180                         /* notifiers for screen redraw */
181                         if(win->screen->do_refresh)
182                                 ED_screen_refresh(C->wm, win);
183                         if(win->screen->do_draw)
184                                 ED_screen_draw(win);
185                         
186                         for(sa= win->screen->areabase.first; sa; sa= sa->next) {
187                                 ARegion *ar= sa->regionbase.first;
188                                 int hasdrawn= 0;
189                                 
190                                 for(; ar; ar= ar->next) {
191                                         hasdrawn |= ar->do_draw;
192                                         
193                                         /* cached notifiers */
194                                         if(ar->do_refresh)
195                                                 ED_region_do_refresh(C, ar);
196                                         
197                                         if(ar->swinid && ar->do_draw)
198                                                 ED_region_do_draw(C, ar);
199                                 }
200                         }
201                         wm_window_swap_buffers(win);
202                 }
203         }
204 }
205
206 /* ********************* handlers *************** */
207
208 /* not handler itself */
209 static void wm_event_free_handler(wmEventHandler *handler)
210 {
211         if(handler->op)
212                 MEM_freeN(handler->op);
213 }
214
215 void wm_event_free_handlers(ListBase *lb)
216 {
217         wmEventHandler *handler;
218         
219         for(handler= lb->first; handler; handler= handler->next)
220                 wm_event_free_handler(handler);
221         
222         BLI_freelistN(lb);
223 }
224
225 static int wm_eventmatch(wmEvent *winevent, wmKeymapItem *km)
226 {
227         if(winevent->type!=km->type) return 0;
228         
229         if(km->val) /* KM_PRESS, KM_RELEASE */
230                 if(winevent->val!=km->val-1) return 0;
231         
232         if(winevent->shift!=km->shift) return 0;
233         if(winevent->ctrl!=km->ctrl) return 0;
234         if(winevent->alt!=km->alt) return 0;
235         if(winevent->oskey!=km->oskey) return 0;
236         if(km->keymodifier)
237                 if(winevent->keymodifier!=km->keymodifier) return 0;
238         
239         /* optional boundbox */
240         
241         return 1;
242 }
243
244 static int wm_handler_operator_call(bContext *C, wmEventHandler *handler, wmEvent *event)
245 {
246         int retval= 0;
247         
248         /* derived, modal or blocking operator */
249         if(handler->op) {
250                 if(handler->op->type->modal)
251                         retval= handler->op->type->modal(C, handler->op, event);
252                 else
253                         printf("wm_handler_operator_call error\n");
254         }
255         else {
256                 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname);
257                 if(ot) {
258                         if(ot->poll==NULL || ot->poll(C)) {
259                                 /* operator on stack, register or new modal handle malloc-copies */
260                                 wmOperator op;
261                                 
262                                 memset(&op, 0, sizeof(wmOperator));
263                                 op.type= ot;
264
265                                 if(op.type->invoke)
266                                         retval= (*op.type->invoke)(C, &op, event);
267                                 else if(&op.type->exec)
268                                         retval= op.type->exec(C, &op);
269                                 
270                                 if( ot->flag & OPTYPE_REGISTER)
271                                         wm_operator_register(C->wm, &op);
272                         }
273                 }
274         }
275         if(retval)
276                 return WM_HANDLER_BREAK;
277         
278         return WM_HANDLER_CONTINUE;
279 }
280
281 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
282 {
283         wmEventHandler *handler;
284         int action= WM_HANDLER_CONTINUE;
285         
286         if(handlers==NULL) return action;
287         
288         for(handler= handlers->first; handler; handler= handler->next) {
289                 if(handler->keymap) {
290                         wmKeymapItem *km;
291                         
292                         for(km= handler->keymap->first; km; km= km->next) {
293                                 if(wm_eventmatch(event, km)) {
294                                         
295                                         if(event->type!=MOUSEMOVE)
296                                                 printf("handle evt %d win %d op %s\n", event->type, C->window->winid, km->idname);
297                                         
298                                         event->keymap_idname= km->idname;       /* weak, but allows interactive callback to not use rawkey */
299                                         
300                                         action= wm_handler_operator_call(C, handler, event);
301                                 }
302                         }
303                         if(action==WM_HANDLER_BREAK)
304                                 break;
305                 }
306                 else {
307                         /* modal, swallows all */
308                         action= wm_handler_operator_call(C, handler, event);
309                 }
310                 
311                 /* modal+blocking handler */
312                 if(handler->flag & WM_HANDLER_BLOCKING)
313                         action= WM_HANDLER_BREAK;
314         }
315         return action;
316 }
317
318 static int wm_event_inside_i(wmEvent *event, rcti *rect)
319 {
320         return BLI_in_rcti(rect, event->x, event->y);
321 }
322
323
324 /* called in main loop */
325 /* goes over entire hierarchy:  events -> window -> screen -> area -> region */
326 void wm_event_do_handlers(bContext *C)
327 {
328         wmWindow *win;
329         
330         for(win= C->wm->windows.first; win; win= win->next) {
331                 wmEvent *event;
332                 
333                 if( win->screen==NULL )
334                         wm_event_free_all(win);
335                 
336                 while( (event=wm_event_next(win)) ) {
337                         int action;
338                         
339                         /* MVC demands to not draw in event handlers... for now we leave it */
340                         /* it also updates context (win, screen) */
341                         wm_window_make_drawable(C, win);
342                         
343                         action= wm_handlers_do(C, event, &win->handlers);
344                         
345                         if(action==WM_HANDLER_CONTINUE) {
346                                 ScrArea *sa= win->screen->areabase.first;
347                                 
348                                 for(; sa; sa= sa->next) {
349                                         if(wm_event_inside_i(event, &sa->totrct)) {
350                                                 
351                                                 C->curarea= sa;
352                                                 action= wm_handlers_do(C, event, &sa->handlers);
353                                                 if(action==WM_HANDLER_CONTINUE) {
354                                                         ARegion *ar= sa->regionbase.first;
355                                                         
356                                                         for(; ar; ar= ar->next) {
357                                                                 if(wm_event_inside_i(event, &ar->winrct)) {
358                                                                         C->region= ar;
359                                                                         action= wm_handlers_do(C, event, &ar->handlers);
360                                                                         if(action==WM_HANDLER_BREAK)
361                                                                                 break;
362                                                                 }
363                                                         }
364                                                 }
365                                                 if(action==WM_HANDLER_BREAK)
366                                                         break;
367                                         }
368                                 }
369                         }
370                         wm_event_free(event);
371                 }
372         }
373 }
374
375 /* lets not expose struct outside wm? */
376 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
377 {
378         handler->flag= flag;
379 }
380
381 wmEventHandler *WM_event_add_modal_handler(ListBase *handlers, wmOperator *op)
382 {
383         /* debug test; operator not in registry */
384         if(op->type->flag & OPTYPE_REGISTER) {
385                 printf("error: handler (%s) cannot be modal, was registered\n", op->type->idname);
386         }
387         else {
388                 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event handler");
389                 wmOperator *opc= MEM_mallocN(sizeof(wmOperator), "operator modal");
390                 
391                 BLI_addhead(handlers, handler);
392                 *opc= *op;
393                 handler->op= opc;
394                 
395                 return handler;
396         }
397         return NULL;
398 }
399
400 void WM_event_remove_modal_handler(ListBase *handlers, wmOperator *op)
401 {
402         wmEventHandler *handler;
403         
404         for(handler= handlers->first; handler; handler= handler->next) {
405                 if(handler->op==op) {
406                         BLI_remlink(handlers, handler);
407                         wm_event_free_handler(handler);
408                         MEM_freeN(handler);
409                         break;
410                 }
411         }
412 }
413
414 wmEventHandler *WM_event_add_keymap_handler(ListBase *keymap, ListBase *handlers)
415 {
416         wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event handler");
417         
418         BLI_addtail(handlers, handler);
419         handler->keymap= keymap;
420         
421         return handler;
422 }
423
424
425 /* ********************* ghost stuff *************** */
426
427 static int convert_key(GHOST_TKey key) 
428 {
429         if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
430                 return (AKEY + ((int) key - GHOST_kKeyA));
431         } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
432                 return (ZEROKEY + ((int) key - GHOST_kKey0));
433         } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
434                 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
435         } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF12) {
436                 return (F1KEY + ((int) key - GHOST_kKeyF1));
437         } else {
438                 switch (key) {
439                         case GHOST_kKeyBackSpace:               return BACKSPACEKEY;
440                         case GHOST_kKeyTab:                             return TABKEY;
441                         case GHOST_kKeyLinefeed:                return LINEFEEDKEY;
442                         case GHOST_kKeyClear:                   return 0;
443                         case GHOST_kKeyEnter:                   return RETKEY;
444                                 
445                         case GHOST_kKeyEsc:                             return ESCKEY;
446                         case GHOST_kKeySpace:                   return SPACEKEY;
447                         case GHOST_kKeyQuote:                   return QUOTEKEY;
448                         case GHOST_kKeyComma:                   return COMMAKEY;
449                         case GHOST_kKeyMinus:                   return MINUSKEY;
450                         case GHOST_kKeyPeriod:                  return PERIODKEY;
451                         case GHOST_kKeySlash:                   return SLASHKEY;
452                                 
453                         case GHOST_kKeySemicolon:               return SEMICOLONKEY;
454                         case GHOST_kKeyEqual:                   return EQUALKEY;
455                                 
456                         case GHOST_kKeyLeftBracket:             return LEFTBRACKETKEY;
457                         case GHOST_kKeyRightBracket:    return RIGHTBRACKETKEY;
458                         case GHOST_kKeyBackslash:               return BACKSLASHKEY;
459                         case GHOST_kKeyAccentGrave:             return ACCENTGRAVEKEY;
460                                 
461                         case GHOST_kKeyLeftShift:               return LEFTSHIFTKEY;
462                         case GHOST_kKeyRightShift:              return RIGHTSHIFTKEY;
463                         case GHOST_kKeyLeftControl:             return LEFTCTRLKEY;
464                         case GHOST_kKeyRightControl:    return RIGHTCTRLKEY;
465                         case GHOST_kKeyCommand:                 return COMMANDKEY;
466                         case GHOST_kKeyLeftAlt:                 return LEFTALTKEY;
467                         case GHOST_kKeyRightAlt:                return RIGHTALTKEY;
468                                 
469                         case GHOST_kKeyCapsLock:                return CAPSLOCKKEY;
470                         case GHOST_kKeyNumLock:                 return 0;
471                         case GHOST_kKeyScrollLock:              return 0;
472                                 
473                         case GHOST_kKeyLeftArrow:               return LEFTARROWKEY;
474                         case GHOST_kKeyRightArrow:              return RIGHTARROWKEY;
475                         case GHOST_kKeyUpArrow:                 return UPARROWKEY;
476                         case GHOST_kKeyDownArrow:               return DOWNARROWKEY;
477                                 
478                         case GHOST_kKeyPrintScreen:             return 0;
479                         case GHOST_kKeyPause:                   return PAUSEKEY;
480                                 
481                         case GHOST_kKeyInsert:                  return INSERTKEY;
482                         case GHOST_kKeyDelete:                  return DELKEY;
483                         case GHOST_kKeyHome:                    return HOMEKEY;
484                         case GHOST_kKeyEnd:                             return ENDKEY;
485                         case GHOST_kKeyUpPage:                  return PAGEUPKEY;
486                         case GHOST_kKeyDownPage:                return PAGEDOWNKEY;
487                                 
488                         case GHOST_kKeyNumpadPeriod:    return PADPERIOD;
489                         case GHOST_kKeyNumpadEnter:             return PADENTER;
490                         case GHOST_kKeyNumpadPlus:              return PADPLUSKEY;
491                         case GHOST_kKeyNumpadMinus:             return PADMINUS;
492                         case GHOST_kKeyNumpadAsterisk:  return PADASTERKEY;
493                         case GHOST_kKeyNumpadSlash:             return PADSLASHKEY;
494                                 
495                         case GHOST_kKeyGrLess:              return GRLESSKEY; 
496                                 
497                         default:
498                                 return UNKNOWNKEY;      /* GHOST_kKeyUnknown */
499                 }
500         }
501 }
502
503 /* adds customdata to event */
504 static void update_tablet_data(wmWindow *win, wmEvent *event)
505 {
506         const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
507         
508         /* if there's tablet data from an active tablet device then add it */
509         if ((td != NULL) && td->Active) {
510                 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
511                 
512                 wmtab->Active = td->Active;
513                 wmtab->Pressure = td->Pressure;
514                 wmtab->Xtilt = td->Xtilt;
515                 wmtab->Ytilt = td->Ytilt;
516                 
517                 event->custom= EVT_TABLET;
518                 event->customdata= wmtab;
519         } 
520 }
521
522
523 /* windows store own event queues, no bContext here */
524 void wm_event_add_ghostevent(wmWindow *win, int type, void *customdata)
525 {
526         wmEvent event, *evt= win->eventstate;
527         
528         /* initialize and copy state (only mouse x y and modifiers) */
529         event= *evt;
530         
531         switch (type) {
532                 /* mouse move */
533                 case GHOST_kEventCursorMove: {
534                         if(win->active) {
535                                 GHOST_TEventCursorData *cd= customdata;
536                                 int cx, cy;
537
538                                 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
539                                 
540                                 event.type= MOUSEMOVE;
541                                 event.x= evt->x= cx;
542                                 event.y= evt->y= (win->sizey-1) - cy;
543                                 
544                                 ED_screen_set_subwinactive(win);        /* state variables in screen */
545                                 
546                                 update_tablet_data(win, &event);
547                                 wm_event_add(win, &event);
548                         }
549                         break;
550                 }
551                 /* mouse button */
552                 case GHOST_kEventButtonDown:
553                 case GHOST_kEventButtonUp: {
554                         GHOST_TEventButtonData *bd= customdata;
555                         event.val= (type==GHOST_kEventButtonDown);
556                         
557                         if (bd->button == GHOST_kButtonMaskLeft)
558                                 event.type= LEFTMOUSE;
559                         else if (bd->button == GHOST_kButtonMaskRight)
560                                 event.type= RIGHTMOUSE;
561                         else
562                                 event.type= MIDDLEMOUSE;
563                         
564                         if(event.val)
565                                 event.keymodifier= evt->keymodifier= event.type;
566                         else
567                                 event.keymodifier= evt->keymodifier= 0;
568                         
569                         update_tablet_data(win, &event);
570                         wm_event_add(win, &event);
571                         
572                         break;
573                 }
574                 /* keyboard */
575                 case GHOST_kEventKeyDown:
576                 case GHOST_kEventKeyUp: {
577                         GHOST_TEventKeyData *kd= customdata;
578                         event.type= convert_key(kd->key);
579                         event.ascii= kd->ascii;
580                         event.val= (type==GHOST_kEventKeyDown);
581                         
582                         /* modifiers */
583                         if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
584                                 event.shift= evt->shift= event.val;
585                         } else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
586                                 event.ctrl= evt->ctrl= event.val;
587                         } else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
588                                 event.alt= evt->alt= event.val;
589                         } else if (event.type==COMMANDKEY) {
590                                 event.oskey= evt->oskey= event.val;
591                         }
592                         
593                         wm_event_add(win, &event);
594                         
595                         break;
596                 }
597                         
598                 case GHOST_kEventWheel: {
599                         GHOST_TEventWheelData* wheelData = customdata;
600                         
601                         if (wheelData->z > 0)
602                                 event.type= WHEELUPMOUSE;
603                         else
604                                 event.type= WHEELDOWNMOUSE;
605                         
606                         event.val= wheelData->z;        /* currently -1 or +1, see ghost for improvements here... */
607                         wm_event_add(win, &event);
608                         
609                         break;
610                 }
611                 case GHOST_kEventUnknown:
612                 case GHOST_kNumEventTypes:
613                         break;
614         }
615 }
616