8537c7453032ed680995805c0d924cefd316125d
[blender-staging.git] / source / blender / windowmanager / intern / wm_draw.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 #include <GL/glew.h>
32
33 #include "DNA_listBase.h"
34 #include "DNA_screen_types.h"
35 #include "DNA_windowmanager_types.h"
36 #include "DNA_userdef_types.h"
37
38 #include "MEM_guardedalloc.h"
39
40 #include "BLI_blenlib.h"
41
42 #include "BKE_context.h"
43 #include "BKE_global.h"
44
45 #include "ED_screen.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49 #include "wm.h"
50 #include "wm_window.h"
51 #include "wm_event_system.h"
52
53 /* swap */
54 #define WIN_NONE_OK             0
55 #define WIN_BACK_OK     1
56 #define WIN_FRONT_OK    2
57 #define WIN_BOTH_OK             3
58
59 /* draw method */
60 #define USER_DRAW_ALL                   0
61 #define USER_DRAW_OVERLAP_ALL   1
62 #define USER_DRAW_OVERLAP               2
63 #define USER_DRAW_TRIPLE                3
64
65 /* ********************* drawing, swap ****************** */
66
67 static void wm_paintcursor_draw(bContext *C)
68 {
69         wmWindowManager *wm= CTX_wm_manager(C);
70         
71         if(wm->paintcursors.first) {
72                 wmWindow *win= CTX_wm_window(C);
73                 wmPaintCursor *pc;
74                 
75                 for(pc= wm->paintcursors.first; pc; pc= pc->next) {
76                         if(pc->poll(C)) {
77                                 ARegion *ar= CTX_wm_region(C);
78                                 pc->draw(C, win->eventstate->x - ar->winrct.xmin, win->eventstate->y - ar->winrct.ymin);
79                         }
80                 }
81         }
82 }
83
84 /********************** draw all **************************/
85 /* - reference method, draw all each time                 */
86
87 static void wm_method_draw_all(bContext *C, wmWindow *win)
88 {
89         bScreen *screen= win->screen;
90         ScrArea *sa;
91         ARegion *ar;
92
93         /* draw area regions */
94         for(sa= screen->areabase.first; sa; sa= sa->next) {
95                 CTX_wm_area_set(C, sa);
96
97                 for(ar=sa->regionbase.first; ar; ar= ar->next) {
98                         if(ar->swinid) {
99                                 CTX_wm_region_set(C, ar);
100                                 ED_region_do_draw(C, ar);
101                                 if(screen->subwinactive==ar->swinid)
102                                         wm_paintcursor_draw(C);
103                                 ED_area_overdraw_flush(C, sa, ar);
104                                 CTX_wm_region_set(C, NULL);
105                         }
106                 }
107                 
108                 CTX_wm_area_set(C, NULL);
109         }
110
111         ED_screen_draw(win);
112         ED_area_overdraw(C);
113
114         /* draw overlapping regions */
115         for(ar=screen->regionbase.first; ar; ar= ar->next) {
116                 if(ar->swinid) {
117                         CTX_wm_region_set(C, ar);
118                         ED_region_do_draw(C, ar);
119                         CTX_wm_region_set(C, NULL);
120                 }
121         }
122
123         if(screen->do_gesture)
124                 wm_gesture_draw(win);
125 }
126
127 /****************** draw overlap all **********************/
128 /* - redraw marked areas, and anything that overlaps it   */
129 /* - it also handles swap exchange optionally, assuming   */
130 /*   that on swap no clearing happens and we get back the */
131 /*   same buffer as we swapped to the front               */
132 /* - TODO for swap exchange in full screen mode, and then */
133 /*   switching to another window seems to invalidate the  */
134 /*   swap flags, probably best to clear then?             */
135
136 /* mark area-regions to redraw if overlapped with rect */
137 static void wm_overlap_regions_down(bScreen *screen, rcti *dirty)
138 {
139         ScrArea *sa;
140         ARegion *ar;
141
142         for(sa= screen->areabase.first; sa; sa= sa->next) {
143                 for(ar= sa->regionbase.first; ar; ar= ar->next) {
144                         if(BLI_isect_rcti(dirty, &ar->winrct, NULL)) {
145                                 ar->do_draw= 1;
146                                 ar->swap= WIN_NONE_OK;
147                         }
148                 }
149         }
150 }
151
152 /* mark menu-regions to redraw if overlapped with rect */
153 static void wm_overlap_regions_up(bScreen *screen, rcti *dirty)
154 {
155         ARegion *ar;
156         
157         for(ar= screen->regionbase.first; ar; ar= ar->next) {
158                 if(BLI_isect_rcti(dirty, &ar->winrct, NULL)) {
159                         ar->do_draw= 1;
160                         ar->swap= WIN_NONE_OK;
161                 }
162         }
163 }
164
165 static void wm_method_draw_overlap_all(bContext *C, wmWindow *win)
166 {
167         bScreen *screen= win->screen;
168         ScrArea *sa;
169         ARegion *ar;
170         int exchange= (G.f & G_SWAP_EXCHANGE);
171
172         /* flush overlapping regions */
173         if(screen->regionbase.first) {
174                 /* flush redraws of area regions up to overlapping regions */
175                 for(sa= screen->areabase.first; sa; sa= sa->next)
176                         for(ar= sa->regionbase.first; ar; ar= ar->next)
177                                 if(ar->swinid && ar->do_draw)
178                                         wm_overlap_regions_up(screen, &ar->winrct);
179                 
180                 /* flush between overlapping regions */
181                 for(ar= screen->regionbase.last; ar; ar= ar->prev)
182                         if(ar->swinid && ar->do_draw)
183                                 wm_overlap_regions_up(screen, &ar->winrct);
184                 
185                 /* flush redraws of overlapping regions down to area regions */
186                 for(ar= screen->regionbase.last; ar; ar= ar->prev)
187                         if(ar->swinid && ar->do_draw)
188                                 wm_overlap_regions_down(screen, &ar->winrct);
189         }
190
191         /* draw marked area regions */
192         for(sa= screen->areabase.first; sa; sa= sa->next) {
193                 CTX_wm_area_set(C, sa);
194
195                 for(ar=sa->regionbase.first; ar; ar= ar->next) {
196                         if(ar->swinid) {
197                                 if(ar->do_draw) {
198                                         CTX_wm_region_set(C, ar);
199                                         ED_region_do_draw(C, ar);
200                                         if(screen->subwinactive==ar->swinid)
201                                                 wm_paintcursor_draw(C);
202                                         ED_area_overdraw_flush(C, sa, ar);
203                                         CTX_wm_region_set(C, NULL);
204
205                                         if(exchange)
206                                                 ar->swap= WIN_FRONT_OK;
207                                 }
208                                 else if(exchange) {
209                                         if(ar->swap == WIN_FRONT_OK) {
210                                                 CTX_wm_region_set(C, ar);
211                                                 ED_region_do_draw(C, ar);
212                                                 if(screen->subwinactive==ar->swinid)
213                                                         wm_paintcursor_draw(C);
214                                                 ED_area_overdraw_flush(C, sa, ar);
215                                                 CTX_wm_region_set(C, NULL);
216
217                                                 ar->swap= WIN_BOTH_OK;
218                                                 printf("draws swap exchange %d\n", ar->swinid);
219                                         }
220                                         else if(ar->swap == WIN_BACK_OK)
221                                                 ar->swap= WIN_FRONT_OK;
222                                         else if(ar->swap == WIN_BOTH_OK)
223                                                 ar->swap= WIN_BOTH_OK;
224                                 }
225                         }
226                 }
227                 
228                 CTX_wm_area_set(C, NULL);
229         }
230
231         /* after area regions so we can do area 'overlay' drawing */
232         if(screen->do_draw) {
233                 ED_screen_draw(win);
234
235                 if(exchange)
236                         screen->swap= WIN_FRONT_OK;
237         }
238         else if(exchange) {
239                 if(screen->swap==WIN_FRONT_OK) {
240                         ED_screen_draw(win);
241                         screen->swap= WIN_BOTH_OK;
242                 }
243                 else if(screen->swap==WIN_BACK_OK)
244                         screen->swap= WIN_FRONT_OK;
245                 else if(screen->swap==WIN_BOTH_OK)
246                         screen->swap= WIN_BOTH_OK;
247         }
248
249         ED_area_overdraw(C);
250
251         /* draw marked overlapping regions */
252         for(ar=screen->regionbase.first; ar; ar= ar->next) {
253                 if(ar->swinid && ar->do_draw) {
254                         CTX_wm_region_set(C, ar);
255                         ED_region_do_draw(C, ar);
256                         CTX_wm_region_set(C, NULL);
257                 }
258         }
259
260         if(screen->do_gesture)
261                 wm_gesture_draw(win);
262 }
263
264 /******************** draw overlap ************************/
265 /* - not implemented                                      */
266
267 static void wm_method_draw_overlap(bContext *C, wmWindow *win)
268 {
269         wm_method_draw_all(C, win);
270 }
271
272 /****************** draw triple buffer ********************/
273 /* - area regions are written into a texture, without any */
274 /*   of the overlapping menus, brushes, gestures. these   */
275 /*   are redrawn each time.                               */
276 /* - work in progress still ..                            */
277 /* - TODO glDeleteTextures ..                             */
278 /* - TODO handle window resize                            */
279 /* - TODO avoid region redraw for brush and gestures..    */
280 /* - TODO use multiple smaller textures for cards without */
281 /*   non power of two support                             */
282
283 static int is_pow2(int n)
284 {
285         return ((n)&(n-1))==0;
286 }
287
288 static int larger_pow2(int n)
289 {
290         if (is_pow2(n))
291                 return n;
292
293         while(!is_pow2(n))
294                 n= n&(n-1);
295
296         return n*2;
297 }
298
299 static void wm_method_draw_triple(bContext *C, wmWindow *win)
300 {
301         wmWindowManager *wm= CTX_wm_manager(C);
302         bScreen *screen= win->screen;
303         ScrArea *sa;
304         ARegion *ar;
305         float halfx, halfy, ratiox, ratioy;
306         int copytex= 0;
307
308         if(win->drawtex) {
309                 glClearColor(0, 0, 0, 0);
310                 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
311
312                 wmSubWindowSet(win, screen->mainwin);
313
314                 /* wmOrtho for the screen has this same offset */
315                 ratiox= win->sizex/(float)win->drawtexw;
316                 ratioy= win->sizey/(float)win->drawtexh;
317                 halfx= 0.375f/win->drawtexw;
318                 halfy= 0.375f/win->drawtexh;
319
320                 glBindTexture(GL_TEXTURE_2D, win->drawtex);
321                 glEnable(GL_TEXTURE_2D);
322
323                 glColor3f(1.0f, 1.0f, 1.0f);
324                 glBegin(GL_QUADS);
325                         glTexCoord2f(halfx, halfy);
326                         glVertex2f(0.0f, 0.0f);
327                         glTexCoord2f(ratiox+halfx, halfy);
328                         glVertex2f(win->sizex, 0.0f);
329                         glTexCoord2f(ratiox+halfx, ratioy+halfy);
330                         glVertex2f(win->sizex, win->sizey);
331                         glTexCoord2f(halfx, ratioy+halfy);
332                         glVertex2f(0.0f, win->sizey);
333                 glEnd();
334
335                 glDisable(GL_TEXTURE_2D);
336                 glBindTexture(GL_TEXTURE_2D, 0);
337         }
338         else {
339                 GLint format;
340
341                 glGenTextures(1, (GLuint *)&win->drawtex);
342
343                 if(!win->drawtex) {
344                         /* not the typical failure case but we handle it anyway */
345                         win->drawmethod= USER_DRAW_OVERLAP_ALL;
346                         wm_method_draw_overlap_all(C, win);
347
348                         printf("failed to allocate texture for triple buffer drawing (generate).\n");
349                         return;
350                 }
351
352                 win->drawtexw= win->sizex;
353                 win->drawtexh= win->sizey;
354
355                 if(!GLEW_ARB_texture_non_power_of_two) {
356                         win->drawtexw= larger_pow2(win->drawtexw);
357                         win->drawtexh= larger_pow2(win->drawtexh);
358                 }
359
360                 glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGB8, win->drawtexw, win->drawtexh, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
361                 glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format);
362
363                 if(format != GL_RGB8) {
364                         /* proxy texture is only guaranteed to test for the cases that
365                          * there is only one texture in use, which may not be the case */
366                         glDeleteTextures(1, (GLuint *)&win->drawtex);
367
368                         win->drawmethod= USER_DRAW_OVERLAP_ALL;
369                         wm_method_draw_overlap_all(C, win);
370
371                         printf("failed to allocate texture for triple buffer drawing (proxy test).\n");
372                         return;
373                 }
374
375                 glBindTexture(GL_TEXTURE_2D, win->drawtex);
376                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, win->drawtexw, win->drawtexh, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
377                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
378                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
379                 glBindTexture(GL_TEXTURE_2D, 0);
380
381                 if(glGetError() == GL_OUT_OF_MEMORY) {
382                         /* not sure if this works everywhere .. */
383                         glDeleteTextures(1, (GLuint *)&win->drawtex);
384
385                         win->drawmethod= USER_DRAW_OVERLAP_ALL;
386                         wm_method_draw_overlap_all(C, win);
387
388                         printf("failed to allocate texture for triple buffer drawing (out of memory).\n");
389                         return;
390                 }
391         }
392
393         /* draw marked area regions */
394         for(sa= screen->areabase.first; sa; sa= sa->next) {
395                 CTX_wm_area_set(C, sa);
396
397                 for(ar=sa->regionbase.first; ar; ar= ar->next) {
398                         if(ar->swinid && ar->do_draw) {
399                                 CTX_wm_region_set(C, ar);
400                                 ED_region_do_draw(C, ar);
401                                 CTX_wm_region_set(C, NULL);
402                                 copytex= 1;
403                         }
404                 }
405                 
406                 CTX_wm_area_set(C, NULL);
407         }
408
409         if(copytex) {
410                 wmSubWindowSet(win, screen->mainwin);
411                 ED_area_overdraw(C);
412
413                 glBindTexture(GL_TEXTURE_2D, win->drawtex);
414                 glReadBuffer(GL_BACK);
415                 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, win->sizex, win->sizey);
416                 glBindTexture(GL_TEXTURE_2D, 0);
417         }
418
419         /* after area regions so we can do area 'overlay' drawing */
420         ED_screen_draw(win);
421
422         /* draw overlapping regions */
423         for(ar=screen->regionbase.first; ar; ar= ar->next) {
424                 if(ar->swinid) {
425                         CTX_wm_region_set(C, ar);
426                         ED_region_do_draw(C, ar);
427                         CTX_wm_region_set(C, NULL);
428                 }
429         }
430
431         if(win->screen->do_gesture)
432                 wm_gesture_draw(win);
433
434         if(wm->paintcursors.first) {
435                 for(sa= screen->areabase.first; sa; sa= sa->next) {
436                         for(ar=sa->regionbase.first; ar; ar= ar->next) {
437                                 if(ar->swinid == screen->subwinactive) {
438                                         CTX_wm_area_set(C, sa);
439                                         CTX_wm_region_set(C, ar);
440
441                                         wmSubWindowSet(win, ar->swinid);
442                                         ED_region_pixelspace(ar);
443                                         wm_paintcursor_draw(C);
444
445                                         CTX_wm_region_set(C, NULL);
446                                         CTX_wm_area_set(C, NULL);
447                                 }
448                         }
449                 }
450
451                 wmSubWindowSet(win, screen->mainwin);
452         }
453 }
454
455 /****************** main update call **********************/
456
457 /* quick test to prevent changing window drawable */
458 static int wm_draw_update_test_window(wmWindow *win)
459 {
460         ScrArea *sa;
461         ARegion *ar;
462         
463         if(win->screen->do_refresh)
464                 return 1;
465         if(win->screen->do_draw)
466                 return 1;
467         if(win->screen->do_gesture)
468                 return 1;
469         
470         for(ar= win->screen->regionbase.first; ar; ar= ar->next)
471                 if(ar->swinid && ar->do_draw)
472                         return 1;
473                 
474         for(sa= win->screen->areabase.first; sa; sa= sa->next)
475                 for(ar=sa->regionbase.first; ar; ar= ar->next)
476                         if(ar->swinid && ar->do_draw)
477                                 return 1;
478
479         return 0;
480 }
481
482 void wm_draw_update(bContext *C)
483 {
484         wmWindowManager *wm= CTX_wm_manager(C);
485         wmWindow *win;
486         
487         for(win= wm->windows.first; win; win= win->next) {
488                 win->drawmethod= USER_DRAW_OVERLAP_ALL;
489
490                 if(wm_draw_update_test_window(win)) {
491                         CTX_wm_window_set(C, win);
492                         
493                         /* sets context window+screen */
494                         wm_window_make_drawable(C, win);
495
496                         /* notifiers for screen redraw */
497                         if(win->screen->do_refresh)
498                                 ED_screen_refresh(wm, win);
499
500                         if(win->drawmethod == USER_DRAW_ALL)
501                                 wm_method_draw_all(C, win);
502                         else if(win->drawmethod == USER_DRAW_OVERLAP_ALL)
503                                 wm_method_draw_overlap_all(C, win);
504                         else if(win->drawmethod == USER_DRAW_OVERLAP)
505                                 wm_method_draw_overlap(C, win);
506                         else if(win->drawmethod == USER_DRAW_TRIPLE)
507                                 wm_method_draw_triple(C, win);
508                 
509                         wm_window_swap_buffers(win);
510
511                         CTX_wm_window_set(C, NULL);
512                 }
513         }
514 }
515