4 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
20 * The Original Code is Copyright (C) 2007 Blender Foundation.
21 * All rights reserved.
24 * Contributor(s): Blender Foundation
26 * ***** END GPL LICENSE BLOCK *****
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 */
37 #include "MEM_guardedalloc.h"
39 #include "GHOST_C-api.h"
41 #include "BLI_blenlib.h"
43 #include "BKE_blender.h"
44 #include "BKE_global.h"
46 #include "ED_screen.h"
49 #include "RNA_access.h"
54 #include "wm_window.h"
55 #include "wm_event_system.h"
56 #include "wm_event_types.h"
58 /* ************ event management ************** */
60 void wm_event_add(wmWindow *win, wmEvent *event_to_add)
62 wmEvent *event= MEM_callocN(sizeof(wmEvent), "event");
64 *event= *event_to_add;
65 BLI_addtail(&win->queue, event);
68 wmEvent *wm_event_next(wmWindow *win)
70 wmEvent *event= win->queue.first;
72 if(event) BLI_remlink(&win->queue, event);
76 static void wm_event_free(wmEvent *event)
78 if(event->customdata && event->customdatafree)
79 MEM_freeN(event->customdata);
83 void wm_event_free_all(wmWindow *win)
87 while((event= win->queue.first)) {
88 BLI_remlink(&win->queue, event);
93 /* ********************* notifiers, listeners *************** */
95 /* XXX: in future, which notifiers to send to other windows? */
96 void WM_event_add_notifier(bContext *C, int type, int value, void *data)
98 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
100 BLI_addtail(&C->wm->queue, note);
102 note->window= C->window;
104 /* catch global notifications here */
106 case WM_NOTE_WINDOW_REDRAW:
107 case WM_NOTE_SCREEN_CHANGED:
110 if(C->region) note->swinid= C->region->swinid;
118 static wmNotifier *wm_notifier_next(wmWindowManager *wm)
120 wmNotifier *note= wm->queue.first;
122 if(note) BLI_remlink(&wm->queue, note);
126 /* called in mainloop */
127 void wm_event_do_notifiers(bContext *C)
131 while( (note=wm_notifier_next(C->wm)) ) {
134 for(win= C->wm->windows.first; win; win= win->next) {
139 C->screen= win->screen;
141 if(note->window && note->window!=win)
143 if(win->screen==NULL)
146 /* printf("notifier win %d screen %s\n", win->winid, win->screen->id.name+2); */
147 ED_screen_do_listen(win, note);
149 for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
150 if(note->swinid && note->swinid!=ar->swinid)
154 ED_region_do_listen(ar, note);
158 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
161 for(ar=sa->regionbase.first; ar; ar= ar->next) {
162 if(note->swinid && note->swinid!=ar->swinid)
166 ED_region_do_listen(ar, note);
177 MEM_freeN(note->data);
182 /* quick test to prevent changing window drawable */
183 static int wm_draw_update_test_window(wmWindow *win)
188 if(win->screen->do_refresh)
190 if(win->screen->do_draw)
192 if(win->screen->do_gesture)
195 for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
196 /* cached notifiers */
199 if(ar->swinid && ar->do_draw)
203 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
204 for(ar=sa->regionbase.first; ar; ar= ar->next) {
205 /* cached notifiers */
208 if(ar->swinid && ar->do_draw)
215 void wm_draw_update(bContext *C)
219 for(win= C->wm->windows.first; win; win= win->next) {
220 if(wm_draw_update_test_window(win)) {
225 C->screen= win->screen;
227 /* sets context window+screen */
228 wm_window_make_drawable(C, win);
230 /* notifiers for screen redraw */
231 if(win->screen->do_refresh)
232 ED_screen_refresh(C->wm, win);
234 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
239 for(ar=sa->regionbase.first; ar; ar= ar->next) {
242 /* cached notifiers */
244 ED_region_do_refresh(C, ar);
246 if(ar->swinid && ar->do_draw) {
247 ED_region_do_draw(C, ar);
253 /* only internal decoration, like AZone */
255 ED_area_do_draw(C, sa);
260 /* move this here so we can do area 'overlay' drawing */
261 if(win->screen->do_draw)
264 /* regions are menus here */
265 for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
268 /* cached notifiers */
270 ED_region_do_refresh(C, ar);
272 if(ar->swinid && ar->do_draw)
273 ED_region_do_draw(C, ar);
278 if(win->screen->do_gesture)
279 wm_gesture_draw(win);
281 wm_window_swap_buffers(win);
289 /* ********************* operators ******************* */
292 int WM_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event)
294 int retval= OPERATOR_PASS_THROUGH;
296 if(ot->poll==NULL || ot->poll(C)) {
297 wmOperator *op= MEM_callocN(sizeof(wmOperator), "wmOperator");
299 /* XXX adding new operator could be function, only happens here now */
301 BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
303 op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
304 RNA_pointer_create(&RNA_WindowManager, &C->wm->id, ot->srna, op, op->ptr);
307 retval= (*op->type->invoke)(C, op, event);
308 else if(op->type->exec)
309 retval= op->type->exec(C, op);
311 if((retval & OPERATOR_FINISHED) && (ot->flag & OPTYPE_REGISTER)) {
312 wm_operator_register(C->wm, op);
315 else if(!(retval & OPERATOR_RUNNING_MODAL)) {
316 wm_operator_free(op);
324 /* ********************* handlers *************** */
327 /* not handler itself, is called by UI to move handlers to other queues, so don't close modal ones */
328 static void wm_event_free_handler(wmEventHandler *handler)
333 /* called on exit or remove area, only here call cancel callback */
334 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
336 wmEventHandler *handler;
338 /* C is zero on freeing database, modal handlers then already were freed */
339 while((handler=handlers->first)) {
340 BLI_remlink(handlers, handler);
342 if(C && handler->op) {
343 if(handler->op->type->cancel)
344 handler->op->type->cancel(C, handler->op);
345 wm_operator_free(handler->op);
347 wm_event_free_handler(handler);
352 static int wm_eventmatch(wmEvent *winevent, wmKeymapItem *kmi)
355 if(winevent->type!=kmi->type) return 0;
358 if(winevent->val!=kmi->val) return 0;
360 if(winevent->shift!=kmi->shift) return 0;
361 if(winevent->ctrl!=kmi->ctrl) return 0;
362 if(winevent->alt!=kmi->alt) return 0;
363 if(winevent->oskey!=kmi->oskey) return 0;
365 if(winevent->keymodifier!=kmi->keymodifier) return 0;
367 /* optional boundbox */
372 /* Warning: this function removes a modal handler, when finished */
373 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
375 int retval= OPERATOR_PASS_THROUGH;
377 /* derived, modal or blocking operator */
379 wmOperator *op= handler->op;
380 wmOperatorType *ot= op->type;
383 /* we set context to where modal handler came from */
384 ScrArea *area= C->area;
385 ARegion *region= C->region;
387 C->area= handler->op_area;
388 C->region= handler->op_region;
390 retval= ot->modal(C, op, event);
392 /* putting back screen context */
396 if((retval & OPERATOR_FINISHED) && (ot->flag & OPTYPE_REGISTER)) {
397 wm_operator_register(C->wm, op);
400 else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
401 wm_operator_free(op);
406 /* remove modal handler, operator itself should have been cancelled and freed */
407 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
408 BLI_remlink(handlers, handler);
409 wm_event_free_handler(handler);
412 /* prevent silly errors from operator users */
413 retval &= ~OPERATOR_PASS_THROUGH;
418 printf("wm_handler_operator_call error\n");
421 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname);
424 retval= WM_operator_invoke(C, ot, event);
427 if(retval & OPERATOR_PASS_THROUGH)
428 return WM_HANDLER_CONTINUE;
430 return WM_HANDLER_BREAK;
433 static int wm_event_always_pass(wmEvent *event)
435 /* some events we always pass on, to ensure proper communication */
436 return (event->type == TIMER || event->type == MESSAGE);
439 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
441 wmEventHandler *handler, *nexthandler;
442 int action= WM_HANDLER_CONTINUE;
444 if(handlers==NULL) return action;
446 /* modal handlers can get removed in this loop, we keep the loop this way */
447 for(handler= handlers->first; handler; handler= nexthandler) {
448 nexthandler= handler->next;
450 /* modal+blocking handler */
451 if(handler->flag & WM_HANDLER_BLOCKING)
452 action= WM_HANDLER_BREAK;
454 if(handler->keymap) {
457 for(kmi= handler->keymap->first; kmi; kmi= kmi->next) {
458 if(wm_eventmatch(event, kmi)) {
459 /* if(event->type!=MOUSEMOVE)
460 printf("handle evt %d win %d op %s\n", event->type, C->window->winid, kmi->idname); */
462 event->keymap_idname= kmi->idname; /* weak, but allows interactive callback to not use rawkey */
464 action= wm_handler_operator_call(C, handlers, handler, event);
465 if(action==WM_HANDLER_BREAK) /* not wm_event_always_pass(event) here, it denotes removed handler */
471 /* modal, swallows all */
472 action= wm_handler_operator_call(C, handlers, handler, event);
475 if(!wm_event_always_pass(event) && action==WM_HANDLER_BREAK)
482 static int wm_event_inside_i(wmEvent *event, rcti *rect)
484 return BLI_in_rcti(rect, event->x, event->y);
487 static int wm_event_prev_inside_i(wmEvent *event, rcti *rect)
489 if(BLI_in_rcti(rect, event->x, event->y))
491 if(event->type==MOUSEMOVE) {
492 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
500 static ScrArea *area_event_inside(bContext *C, wmEvent *event)
505 for(sa= C->screen->areabase.first; sa; sa= sa->next)
506 if(BLI_in_rcti(&sa->totrct, event->x, event->y))
511 static ARegion *region_event_inside(bContext *C, wmEvent *event)
515 if(C->screen && C->area)
516 for(ar= C->area->regionbase.first; ar; ar= ar->next)
517 if(BLI_in_rcti(&ar->winrct, event->x, event->y))
523 /* called in main loop */
524 /* goes over entire hierarchy: events -> window -> screen -> area -> region */
525 void wm_event_do_handlers(bContext *C)
529 for(win= C->wm->windows.first; win; win= win->next) {
532 if( win->screen==NULL )
533 wm_event_free_all(win);
535 while( (event=wm_event_next(win)) ) {
539 C->screen= win->screen;
540 C->area= area_event_inside(C, event);
541 C->region= region_event_inside(C, event);
543 /* MVC demands to not draw in event handlers... for now we leave it */
544 wm_window_make_drawable(C, win);
546 action= wm_handlers_do(C, event, &win->handlers);
548 /* modal menus in Blender use (own) regions linked to screen */
549 if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) {
552 /* region are in drawing order, i.e. frontmost region last so
553 * we handle events in the opposite order last to first */
554 for(ar=win->screen->regionbase.last; ar; ar= ar->prev) {
555 if(wm_event_always_pass(event) || wm_event_inside_i(event, &ar->winrct)) {
557 wm_handlers_do(C, event, &ar->handlers);
560 if(!wm_event_always_pass(event)) {
561 action= WM_HANDLER_BREAK;
568 if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) {
573 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
574 if(wm_event_always_pass(event) || wm_event_prev_inside_i(event, &sa->totrct)) {
577 action= wm_handlers_do(C, event, &sa->handlers);
579 if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) {
580 for(ar=sa->regionbase.first; ar; ar= ar->next) {
581 if(wm_event_always_pass(event) || wm_event_inside_i(event, &ar->winrct)) {
583 action= wm_handlers_do(C, event, &ar->handlers);
586 if(!wm_event_always_pass(event)) {
587 if(action==WM_HANDLER_BREAK)
595 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
598 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad?
599 doing it on ghost queue gives errors when mousemoves go over area borders */
601 C->window->eventstate->prevx= event->x;
602 C->window->eventstate->prevy= event->y;
605 wm_event_free(event);
613 /* lets not expose struct outside wm? */
614 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
619 wmEventHandler *WM_event_add_modal_handler(bContext *C, ListBase *handlers, wmOperator *op)
621 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event handler");
623 handler->op_area= C->area; /* means frozen screen context for modal handlers! */
624 handler->op_region= C->region;
626 BLI_addhead(handlers, handler);
631 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, ListBase *keymap)
633 wmEventHandler *handler;
635 /* only allow same keymap once */
636 for(handler= handlers->first; handler; handler= handler->next)
637 if(handler->keymap==keymap)
640 handler= MEM_callocN(sizeof(wmEventHandler), "event handler");
641 BLI_addtail(handlers, handler);
642 handler->keymap= keymap;
647 void WM_event_remove_keymap_handler(ListBase *handlers, ListBase *keymap)
649 wmEventHandler *handler;
651 for(handler= handlers->first; handler; handler= handler->next) {
652 if(handler->keymap==keymap) {
653 BLI_remlink(handlers, handler);
654 wm_event_free_handler(handler);
661 void WM_event_add_message(wmWindowManager *wm, void *customdata, short customdatafree)
666 for(win=wm->windows.first; win; win=win->next) {
667 event= *(win->eventstate);
671 event.custom= EVT_DATA_MESSAGE;
672 event.customdata= customdata;
673 event.customdatafree= customdatafree;
675 wm_event_add(win, &event);
679 void WM_event_add_mousemove(bContext *C)
681 wmEvent event= *(C->window->eventstate);
682 event.type= MOUSEMOVE;
683 wm_event_add(C->window, &event);
687 /* ********************* ghost stuff *************** */
689 static int convert_key(GHOST_TKey key)
691 if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
692 return (AKEY + ((int) key - GHOST_kKeyA));
693 } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
694 return (ZEROKEY + ((int) key - GHOST_kKey0));
695 } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
696 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
697 } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF12) {
698 return (F1KEY + ((int) key - GHOST_kKeyF1));
701 case GHOST_kKeyBackSpace: return BACKSPACEKEY;
702 case GHOST_kKeyTab: return TABKEY;
703 case GHOST_kKeyLinefeed: return LINEFEEDKEY;
704 case GHOST_kKeyClear: return 0;
705 case GHOST_kKeyEnter: return RETKEY;
707 case GHOST_kKeyEsc: return ESCKEY;
708 case GHOST_kKeySpace: return SPACEKEY;
709 case GHOST_kKeyQuote: return QUOTEKEY;
710 case GHOST_kKeyComma: return COMMAKEY;
711 case GHOST_kKeyMinus: return MINUSKEY;
712 case GHOST_kKeyPeriod: return PERIODKEY;
713 case GHOST_kKeySlash: return SLASHKEY;
715 case GHOST_kKeySemicolon: return SEMICOLONKEY;
716 case GHOST_kKeyEqual: return EQUALKEY;
718 case GHOST_kKeyLeftBracket: return LEFTBRACKETKEY;
719 case GHOST_kKeyRightBracket: return RIGHTBRACKETKEY;
720 case GHOST_kKeyBackslash: return BACKSLASHKEY;
721 case GHOST_kKeyAccentGrave: return ACCENTGRAVEKEY;
723 case GHOST_kKeyLeftShift: return LEFTSHIFTKEY;
724 case GHOST_kKeyRightShift: return RIGHTSHIFTKEY;
725 case GHOST_kKeyLeftControl: return LEFTCTRLKEY;
726 case GHOST_kKeyRightControl: return RIGHTCTRLKEY;
727 case GHOST_kKeyCommand: return COMMANDKEY;
728 case GHOST_kKeyLeftAlt: return LEFTALTKEY;
729 case GHOST_kKeyRightAlt: return RIGHTALTKEY;
731 case GHOST_kKeyCapsLock: return CAPSLOCKKEY;
732 case GHOST_kKeyNumLock: return 0;
733 case GHOST_kKeyScrollLock: return 0;
735 case GHOST_kKeyLeftArrow: return LEFTARROWKEY;
736 case GHOST_kKeyRightArrow: return RIGHTARROWKEY;
737 case GHOST_kKeyUpArrow: return UPARROWKEY;
738 case GHOST_kKeyDownArrow: return DOWNARROWKEY;
740 case GHOST_kKeyPrintScreen: return 0;
741 case GHOST_kKeyPause: return PAUSEKEY;
743 case GHOST_kKeyInsert: return INSERTKEY;
744 case GHOST_kKeyDelete: return DELKEY;
745 case GHOST_kKeyHome: return HOMEKEY;
746 case GHOST_kKeyEnd: return ENDKEY;
747 case GHOST_kKeyUpPage: return PAGEUPKEY;
748 case GHOST_kKeyDownPage: return PAGEDOWNKEY;
750 case GHOST_kKeyNumpadPeriod: return PADPERIOD;
751 case GHOST_kKeyNumpadEnter: return PADENTER;
752 case GHOST_kKeyNumpadPlus: return PADPLUSKEY;
753 case GHOST_kKeyNumpadMinus: return PADMINUS;
754 case GHOST_kKeyNumpadAsterisk: return PADASTERKEY;
755 case GHOST_kKeyNumpadSlash: return PADSLASHKEY;
757 case GHOST_kKeyGrLess: return GRLESSKEY;
760 return UNKNOWNKEY; /* GHOST_kKeyUnknown */
765 /* adds customdata to event */
766 static void update_tablet_data(wmWindow *win, wmEvent *event)
768 const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
770 /* if there's tablet data from an active tablet device then add it */
771 if ((td != NULL) && td->Active) {
772 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
774 wmtab->Active = td->Active;
775 wmtab->Pressure = td->Pressure;
776 wmtab->Xtilt = td->Xtilt;
777 wmtab->Ytilt = td->Ytilt;
779 event->custom= EVT_DATA_TABLET;
780 event->customdata= wmtab;
781 event->customdatafree= 1;
786 /* windows store own event queues, no bContext here */
787 void wm_event_add_ghostevent(wmWindow *win, int type, void *customdata)
789 wmEvent event, *evt= win->eventstate;
791 /* initialize and copy state (only mouse x y and modifiers) */
796 case GHOST_kEventCursorMove: {
798 GHOST_TEventCursorData *cd= customdata;
801 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
803 event.type= MOUSEMOVE;
805 event.y= evt->y= (win->sizey-1) - cy;
807 ED_screen_set_subwinactive(win); /* state variables in screen */
809 update_tablet_data(win, &event);
810 wm_event_add(win, &event);
815 case GHOST_kEventButtonDown:
816 case GHOST_kEventButtonUp: {
817 GHOST_TEventButtonData *bd= customdata;
818 event.val= (type==GHOST_kEventButtonDown);
820 if (bd->button == GHOST_kButtonMaskLeft)
821 event.type= LEFTMOUSE;
822 else if (bd->button == GHOST_kButtonMaskRight)
823 event.type= RIGHTMOUSE;
825 event.type= MIDDLEMOUSE;
828 event.keymodifier= evt->keymodifier= event.type;
830 event.keymodifier= evt->keymodifier= 0;
832 update_tablet_data(win, &event);
833 wm_event_add(win, &event);
838 case GHOST_kEventKeyDown:
839 case GHOST_kEventKeyUp: {
840 GHOST_TEventKeyData *kd= customdata;
841 event.type= convert_key(kd->key);
842 event.ascii= kd->ascii;
843 event.val= (type==GHOST_kEventKeyDown); /* XXX eventmatch uses defines, bad code... */
846 if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
847 event.shift= evt->shift= event.val;
848 } else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
849 event.ctrl= evt->ctrl= event.val;
850 } else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
851 event.alt= evt->alt= event.val;
852 } else if (event.type==COMMANDKEY) {
853 event.oskey= evt->oskey= event.val;
856 wm_event_add(win, &event);
861 case GHOST_kEventWheel: {
862 GHOST_TEventWheelData* wheelData = customdata;
864 if (wheelData->z > 0)
865 event.type= WHEELUPMOUSE;
867 event.type= WHEELDOWNMOUSE;
869 event.val= wheelData->z; /* currently -1 or +1, see ghost for improvements here... */
870 wm_event_add(win, &event);
874 case GHOST_kEventTimer: {
876 event.custom= EVT_DATA_TIMER;
877 event.customdata= customdata;
878 wm_event_add(win, &event);
883 case GHOST_kEventUnknown:
884 case GHOST_kNumEventTypes: