Two in one:
[blender.git] / source / blender / src / mywindow.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL 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. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  *
32  *
33  * for compatibility with old iris code, replacement of swinopen, winset, etc
34  * btw: subwindows in X are way too slow, tried it, and choose for my own system... (ton)
35  * 
36  */
37
38 #include <string.h>
39
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
43
44 #include "MEM_guardedalloc.h"
45
46 #include "BLI_blenlib.h"
47 #include "BLI_arithb.h"
48 #include "BLI_gsqueue.h"
49
50 #include "DNA_screen_types.h"
51
52 #include "BKE_global.h"
53
54 #include "BIF_gl.h"
55 #include "BIF_glutil.h"
56 #include "BIF_mywindow.h"
57 #include "BIF_screen.h"
58
59 #include "mydevice.h"
60 #include "blendef.h"
61
62 #include "winlay.h"
63
64 typedef struct {
65         struct bWindow *next, *prev;
66         int id, pad;
67         
68         int xmin, xmax, ymin, ymax;
69         float viewmat[4][4], winmat[4][4];
70
71         GSQueue *qevents;
72 } bWindow;
73
74 /* globals */
75 static Window *winlay_mainwindow;
76 static int curswin=0;
77 static bWindow *swinarray[MAXWIN]= {NULL};
78 static bWindow mainwindow, renderwindow;
79 static int mainwin_color_depth;
80
81 /* prototypes --------------- */
82 void mywindow_init_mainwin(Window *, int , int , int , int );
83 void mywindow_build_and_set_renderwin( int , int , int , int);
84
85 void mywindow_init_mainwin(Window *win, int orx, int ory, int sizex, int sizey)
86 {
87         GLint r, g, b;
88         
89         winlay_mainwindow= win;
90         
91         swinarray[1]= &mainwindow;
92         curswin= 1;
93
94         mainwindow.xmin= orx;
95         mainwindow.ymin= ory;
96         mainwindow.xmax= orx+sizex-1;
97         mainwindow.ymax= ory+sizey-1;
98         mainwindow.qevents= NULL;
99
100         myortho2(-0.375, (float)sizex-0.375, -0.375, (float)sizey-0.375);
101         glLoadIdentity();
102                 
103         glGetFloatv(GL_PROJECTION_MATRIX, (float *)mainwindow.winmat);
104         glGetFloatv(GL_MODELVIEW_MATRIX, (float *)mainwindow.viewmat);
105         
106         glGetIntegerv(GL_RED_BITS, &r);
107         glGetIntegerv(GL_GREEN_BITS, &g);
108         glGetIntegerv(GL_BLUE_BITS, &b);
109
110         mainwin_color_depth= r + g + b;
111         if(G.f & G_DEBUG) {
112                 printf("Color depth r %d g %d b %d\n", (int)r, (int)g, (int)b);
113                 glGetIntegerv(GL_AUX_BUFFERS, &r);
114                 printf("Aux buffers: %d\n", (int)r);
115         }
116 }
117
118 /* XXXXXXXXXXXXXXXX very hacky, because of blenderwindows vs ghostwindows vs renderwindow
119    this routine sets up a blenderwin (a mywin)
120  */
121 void mywindow_build_and_set_renderwin( int orx, int ory, int sizex, int sizey)
122 {
123
124         swinarray[2]= &renderwindow;
125         curswin= 2; /* myortho2 needs this to be set */
126
127         renderwindow.xmin= orx;
128         renderwindow.ymin= ory;
129         renderwindow.xmax= orx+sizex-1;
130         renderwindow.ymax= ory+sizey-1;
131         renderwindow.qevents= NULL;
132
133         myortho2(-0.375, (float)sizex-0.375, -0.375, (float)sizey-0.375);
134         glLoadIdentity();
135
136         glGetFloatv(GL_PROJECTION_MATRIX, (float *)renderwindow.winmat);
137         glGetFloatv(GL_MODELVIEW_MATRIX, (float *)renderwindow.viewmat);
138         mywinset(2);
139
140 }
141
142 /* ------------------------------------------------------------------------- */
143
144         /* XXXXX, remove later */
145 static bWindow *bwin_from_winid(int winid)
146 {
147         bWindow *bwin= swinarray[winid];
148         if (!bwin) {
149                 printf("bwin_from_winid: Internal error, bad winid: %d\n", winid);
150         }
151         return bwin;
152 }
153
154 int bwin_qtest(int winid)
155 {
156         return !BLI_gsqueue_is_empty(bwin_from_winid(winid)->qevents);
157 }
158 int bwin_qread(int winid, BWinEvent *evt_r)
159 {
160         if (bwin_qtest(winid)) {
161                 BLI_gsqueue_pop(bwin_from_winid(winid)->qevents, evt_r);
162                 return 1;
163         } else {
164                 return 0;
165         }
166 }
167 void bwin_qadd(int winid, BWinEvent *evt)
168 {
169         BLI_gsqueue_push(bwin_from_winid(winid)->qevents, evt);
170 }
171
172 /* ------------------------------------------------------------------------- */
173
174 void bwin_get_rect(int winid, rcti *rect_r)
175 {
176         bWindow *win= bwin_from_winid(winid);
177         if(win) {
178                 rect_r->xmin= win->xmin;
179                 rect_r->ymin= win->ymin;
180                 rect_r->xmax= win->xmax;
181                 rect_r->ymax= win->ymax;
182         }
183 }
184
185 void bwin_getsize(int win, int *x, int *y) 
186 {
187         if(win<4) {
188                 if (win==1) {
189                         window_get_size(winlay_mainwindow, x, y);
190                 } else {
191                         printf("bwin_getsize: Internal error, bad winid: %d\n", win);
192                         *x= *y= 0;
193                 }
194         } else {
195                 bWindow *bwin= swinarray[win];
196                 if (bwin) {
197                         *x= bwin->xmax-bwin->xmin+1;
198                         *y= bwin->ymax-bwin->ymin+1;
199                 }
200         }
201 }
202
203 void bwin_getsuborigin(int win, int *x, int *y)
204 {
205         if(win<4) {
206                 *x= *y= 0;      
207         } else {
208                 bWindow *bwin= swinarray[win];
209                 if (bwin) {
210                         *x= bwin->xmin;
211                         *y= bwin->ymin;
212                 }
213         }
214 }
215
216 void bwin_getsinglematrix(int winid, float mat[][4])
217 {
218         bWindow *win;
219         float matview[4][4], matproj[4][4];
220
221         win= swinarray[winid];
222         if(win==NULL) {
223                 glGetFloatv(GL_PROJECTION_MATRIX, (float *)matproj);
224                 glGetFloatv(GL_MODELVIEW_MATRIX, (float *)matview);
225                 Mat4MulMat4(mat, matview, matproj);
226         }
227         else {
228                 Mat4MulMat4(mat, win->viewmat, win->winmat);
229         }
230 }
231
232 /* ------------------------------------------------------------------------- */
233
234 void bwin_load_viewmatrix(int winid, float mat[][4])
235 {
236         bWindow *win= bwin_from_winid(winid);
237         if(win) {
238                 glLoadMatrixf(mat);
239                 Mat4CpyMat4(win->viewmat, mat);
240         }
241 }
242 void bwin_load_winmatrix(int winid, float mat[][4])
243 {
244         bWindow *win= bwin_from_winid(winid);
245         if(win) {
246                 glLoadMatrixf(mat);
247                 Mat4CpyMat4(win->winmat, mat);
248         }
249 }
250
251 void bwin_get_viewmatrix(int winid, float mat[][4])
252 {
253         bWindow *win= bwin_from_winid(winid);
254         if(win)
255                 Mat4CpyMat4(mat, win->viewmat);
256 }
257 void bwin_get_winmatrix(int winid, float mat[][4])
258 {
259         bWindow *win= bwin_from_winid(winid);
260         if(win)
261                 Mat4CpyMat4(mat, win->winmat);
262 }
263
264 void bwin_multmatrix(int winid, float mat[][4])
265 {
266         bWindow *win= bwin_from_winid(winid);
267
268         if(win) {
269                 glMultMatrixf((float*) mat);
270                 glGetFloatv(GL_MODELVIEW_MATRIX, (float *)win->viewmat);
271         }
272 }
273
274 void bwin_scalematrix(int winid, float x, float y, float z)
275 {
276         bWindow *win= bwin_from_winid(winid);
277         if(win) {
278                 glScalef(x, y, z);
279                 glGetFloatv(GL_MODELVIEW_MATRIX, (float *)win->viewmat);
280         }
281 }
282
283 void bwin_clear_viewmat(int swin)
284 {
285         bWindow *win;
286
287         win= swinarray[swin];
288         if(win==NULL) return;
289
290         memset(win->viewmat, 0, sizeof(win->viewmat));
291         win->viewmat[0][0]= 1.0;
292         win->viewmat[1][1]= 1.0;
293         win->viewmat[2][2]= 1.0;
294         win->viewmat[3][3]= 1.0;
295 }
296
297
298 void myloadmatrix(float mat[][4])
299 {
300         if (glaGetOneInteger(GL_MATRIX_MODE)==GL_MODELVIEW) {
301                 bwin_load_viewmatrix(curswin, mat);
302         } else {
303                 bwin_load_winmatrix(curswin, mat);
304         }
305 }
306
307 void mygetmatrix(float mat[][4])
308 {
309         if (glaGetOneInteger(GL_MATRIX_MODE)==GL_MODELVIEW) {
310                 bwin_get_viewmatrix(curswin, mat);
311         } else {
312                 bwin_get_winmatrix(curswin, mat);
313         }
314 }
315
316 void mymultmatrix(float mat[][4])
317 {
318         bwin_multmatrix(curswin, mat);
319 }
320
321 void mygetsingmatrix(float mat[][4])
322 {
323         bwin_getsinglematrix(curswin, mat);
324 }
325
326 int mywinget(void)
327 {
328         return curswin;
329 }
330
331 void mywinset(int wid)
332 {
333         bWindow *win;
334
335         win= swinarray[wid];
336         if(win==NULL) {
337                 printf("mywinset %d: doesn't exist\n", wid);
338                 return;
339         }
340         if (wid == 1 || wid == 2) {     /* main window or renderwindow*/
341                 glViewport(0,  0, ( win->xmax-win->xmin)+1, ( win->ymax-win->ymin)+1);
342                 glScissor(0,  0, ( win->xmax-win->xmin)+1, ( win->ymax-win->ymin)+1);
343         }
344         else {
345                 int width= (win->xmax - win->xmin)+1;
346                 int height= (win->ymax - win->ymin)+1;
347
348                         /* CRITICAL, this clamping ensures that
349                          * the viewport never goes outside the screen
350                          * edges (assuming the x, y coords aren't
351                          * outside). This causes a hardware lock
352                          * on Matrox cards if it happens.
353                          * 
354                          * Really Blender should never _ever_ try
355                          * to do such a thing, but just to be safe
356                          * clamp it anyway (or fix the bScreen
357                          * scaling routine, and be damn sure you
358                          * fixed it). - zr
359                          */
360                 if (win->xmin + width>G.curscreen->sizex)
361                         width= G.curscreen->sizex - win->xmin;
362                 if (win->ymin + height>G.curscreen->sizey)
363                         height= G.curscreen->sizey - win->ymin;
364                 
365                 glViewport(win->xmin, win->ymin, width, height);
366                 glScissor(win->xmin, win->ymin, width, height);
367         }
368         
369         glMatrixMode(GL_PROJECTION);
370         glLoadMatrixf(&win->winmat[0][0]);
371         glMatrixMode(GL_MODELVIEW);
372         glLoadMatrixf(&win->viewmat[0][0]);
373
374         glFlush();
375         
376         curswin= wid;
377 }
378
379 int myswinopen(int parentid, int xmin, int xmax, int ymin, int ymax)
380 {
381         bWindow *win= NULL;
382         int freewinid;
383         
384         for (freewinid= 4; freewinid<MAXWIN; freewinid++)
385                 if (!swinarray[freewinid])
386                         break;
387         
388         /* this case is not handled in Blender, so will crash. use myswinopen_allowed() first to check */
389         if (freewinid==MAXWIN) {
390                 printf("too many windows\n");
391
392                 return 0;
393         } 
394         else {
395                 win= MEM_callocN(sizeof(*win), "winopen");
396
397                 win->id= freewinid;
398                 swinarray[win->id]= win;
399
400                 win->xmin= xmin;
401                 win->ymin= ymin;
402                 win->xmax= xmax;
403                 win->ymax= ymax;
404         
405                 win->qevents= BLI_gsqueue_new(sizeof(BWinEvent));
406
407                 Mat4One(win->viewmat);
408                 Mat4One(win->winmat);
409         
410                 mywinset(win->id);
411
412                 return win->id;
413         }
414 }
415
416 int myswinopen_allowed(void)
417 {
418         int totfree=0;
419         int freewinid;
420         
421         for (freewinid= 4; freewinid<MAXWIN; freewinid++)
422                 if (swinarray[freewinid]==NULL)
423                         totfree++;
424         if(totfree<2)
425                 return 0;
426         return 1;
427 }
428
429 void mywinclose(int winid)
430 {
431         if (winid<4) {
432                 if (winid==1) {
433                         window_destroy(winlay_mainwindow);
434                         winlay_mainwindow= NULL;
435                 } else {
436                         printf("mwinclose: Internal error, bad winid: %d\n", winid);
437                 }
438         } else {
439                 bWindow *win= swinarray[winid];
440
441                 if (win) {
442                         BLI_gsqueue_free(win->qevents);
443                         MEM_freeN(win);
444                 } else {
445                         printf("mwinclose: Internal error, bad winid: %d\n", winid);
446                 }
447         }
448
449         swinarray[winid]= NULL;
450         if (curswin==winid) curswin= 0;
451 }
452
453 void mywinposition(int winid, int xmin, int xmax, int ymin, int ymax) /* watch: syntax differs from iris */
454 {
455         bWindow *win= bwin_from_winid(winid);
456         if(win) {
457                 win->xmin= xmin;
458                 win->ymin= ymin;
459                 win->xmax= xmax;
460                 win->ymax= ymax;
461         }
462 }
463
464
465 void bwin_ortho(int winid, float x1, float x2, float y1, float y2, float n, float f)
466 {
467         bWindow *bwin= bwin_from_winid(winid);
468         if(bwin) {
469                 
470                 glMatrixMode(GL_PROJECTION);
471                 glLoadIdentity();
472
473                 glOrtho(x1, x2, y1, y2, n, f);
474
475                 glGetFloatv(GL_PROJECTION_MATRIX, (float *)bwin->winmat);
476                 glMatrixMode(GL_MODELVIEW);
477         }
478 }
479
480 void bwin_ortho2(int win, float x1, float x2, float y1, float y2)
481 {
482         bwin_ortho(win, x1, x2, y1, y2, -1, 1);
483 }
484
485 void bwin_frustum(int winid, float x1, float x2, float y1, float y2, float n, float f)
486 {
487         bWindow *win= bwin_from_winid(winid);
488         if(win) {
489
490                 glMatrixMode(GL_PROJECTION);
491                 glLoadIdentity();
492                 glFrustum(x1, x2, y1, y2, n, f);
493
494                 glGetFloatv(GL_PROJECTION_MATRIX, (float *)win->winmat);
495                 glMatrixMode(GL_MODELVIEW);
496         }
497 }
498
499 void myortho(float x1, float x2, float y1, float y2, float n, float f)
500 {
501         bwin_ortho(curswin, x1, x2, y1, y2, n, f);
502 }
503
504 void myortho2(float x1, float x2, float y1, float y2)
505 {
506         /* prevent opengl from generating errors */
507         if(x1==x2) x2+=1.0;
508         if(y1==y2) y2+=1.0;
509         bwin_ortho(curswin, x1, x2, y1, y2, -100, 100);
510 }
511
512 void mywindow(float x1, float x2, float y1, float y2, float n, float f)
513 {
514         bwin_frustum(curswin, x1, x2, y1, y2, n, f);
515 }
516
517 #ifdef __APPLE__
518
519 /* apple seems to round colors to below and up on some configs */
520
521 static unsigned int index_to_framebuffer(int index)
522 {
523         unsigned int i= index;
524
525         switch(mainwin_color_depth) {
526         case 12:
527                 i= ((i & 0xF00)<<12) + ((i & 0xF0)<<8) + ((i & 0xF)<<4);
528                 /* sometimes dithering subtracts! */
529                 i |= 0x070707;
530                 break;
531         case 15:
532         case 16:
533                 i= ((i & 0x7C00)<<9) + ((i & 0x3E0)<<6) + ((i & 0x1F)<<3);
534                 i |= 0x030303;
535                 break;
536         case 24:
537                 break;
538         default:        // 18 bits... 
539                 i= ((i & 0x3F000)<<6) + ((i & 0xFC0)<<4) + ((i & 0x3F)<<2);
540                 i |= 0x010101;
541                 break;
542         }
543         
544         return i;
545 }
546
547 #else
548
549 /* this is the old method as being in use for ages.... seems to work? colors are rounded to lower values */
550
551 static unsigned int index_to_framebuffer(int index)
552 {
553         unsigned int i= index;
554         
555         switch(mainwin_color_depth) {
556                 case 8:
557                         i= ((i & 48)<<18) + ((i & 12)<<12) + ((i & 3)<<6);
558                         i |= 0x3F3F3F;
559                         break;
560                 case 12:
561                         i= ((i & 0xF00)<<12) + ((i & 0xF0)<<8) + ((i & 0xF)<<4);
562                         /* sometimes dithering subtracts! */
563                         i |= 0x0F0F0F;
564                         break;
565                 case 15:
566                 case 16:
567                         i= ((i & 0x7C00)<<9) + ((i & 0x3E0)<<6) + ((i & 0x1F)<<3);
568                         i |= 0x070707;
569                         break;
570                 case 24:
571                         break;
572                 default:        // 18 bits... 
573                         i= ((i & 0x3F000)<<6) + ((i & 0xFC0)<<4) + ((i & 0x3F)<<2);
574                         i |= 0x030303;
575                         break;
576         }
577         
578         return i;
579 }
580
581 #endif
582
583 void set_framebuffer_index_color(int index)
584 {
585         cpack(index_to_framebuffer(index));
586 }
587
588 int framebuffer_to_index(unsigned int col)
589 {
590         if (col==0) return 0;
591
592         switch(mainwin_color_depth) {
593         case 8:
594                 return ((col & 0xC00000)>>18) + ((col & 0xC000)>>12) + ((col & 0xC0)>>6);
595         case 12:
596                 return ((col & 0xF00000)>>12) + ((col & 0xF000)>>8) + ((col & 0xF0)>>4);
597         case 15:
598         case 16:
599                 return ((col & 0xF80000)>>9) + ((col & 0xF800)>>6) + ((col & 0xF8)>>3);
600         case 24:
601                 return col & 0xFFFFFF;
602         default: // 18 bits...
603                 return ((col & 0xFC0000)>>6) + ((col & 0xFC00)>>4) + ((col & 0xFC)>>2);
604         }               
605 }
606
607
608 /* ********** END MY WINDOW ************** */
609
610 #ifdef WIN32
611 static int is_a_really_crappy_nvidia_card(void) {
612         static int well_is_it= -1;
613
614                 /* Do you understand the implication? Do you? */
615         if (well_is_it==-1)
616                 well_is_it= (strcmp((char*) glGetString(GL_VENDOR), "NVIDIA Corporation") == 0);
617
618         return well_is_it;
619 }
620 #endif
621
622 void myswapbuffers(void)
623 {
624         ScrArea *sa;
625         
626         sa= G.curscreen->areabase.first;
627         while(sa) {
628                 if(sa->win_swap==WIN_BACK_OK) sa->win_swap= WIN_FRONT_OK;
629                 if(sa->head_swap==WIN_BACK_OK) sa->head_swap= WIN_FRONT_OK;
630                 
631                 sa= sa->next;
632         }
633
634         /* HACK, some windows drivers feel they should honor the scissor
635          * test when swapping buffers, disable the test while swapping
636          * on WIN32. (namely Matrox and NVidia's new drivers around Oct 1 2001)
637          * - zr
638          */
639
640 #ifdef WIN32
641                 /* HACK, in some NVidia driver release some kind of
642                  * fancy optimiziation (I presume) was put in which for
643                  * some reason causes parts of the buffer not to be
644                  * swapped. One way to defeat it is the following wierd
645                  * code (which we only do for nvidia cards). This should
646                  * be removed if NVidia fixes their drivers. - zr
647                  */
648         if (is_a_really_crappy_nvidia_card()) {
649                 glDrawBuffer(GL_FRONT);
650
651                 glBegin(GL_LINES);
652                 glEnd();
653
654                 glDrawBuffer(GL_BACK);
655         }
656
657         glDisable(GL_SCISSOR_TEST);
658         window_swap_buffers(winlay_mainwindow);
659         glEnable(GL_SCISSOR_TEST);
660 #else
661         window_swap_buffers(winlay_mainwindow);
662 #endif
663 }
664
665
666 /* *********************** PATTERNS ETC ***************** */
667
668 void setlinestyle(int nr)
669 {
670         if(nr==0) {
671                 glDisable(GL_LINE_STIPPLE);
672         }
673         else {
674                 
675                 glEnable(GL_LINE_STIPPLE);
676                 glLineStipple(nr, 0xAAAA);
677         }
678 }
679
680 /*******************/
681 /*******************/
682 /*  Menu utilities */
683
684 static int *frontbuffer_save= NULL;
685 static int ov_x, ov_y, ov_sx, ov_sy;
686
687 /*
688 #if defined(__sgi) || defined(__sun) || defined(__sun__) || defined (__sparc) || defined (__sparc__)
689 */
690 /* this is a dirty patch: gets sometimes the backbuffer */
691 /* my_get_frontbuffer_image(0, 0, 1, 1);
692 my_put_frontbuffer_image();
693 #endif
694 */
695
696 void my_put_frontbuffer_image(void)
697 {
698         if (frontbuffer_save) {
699                 glRasterPos2f( (float)ov_x -0.5,  (float)ov_y - 0.5 );
700                 glDrawPixels(ov_sx, ov_sy, GL_RGBA, GL_UNSIGNED_BYTE, frontbuffer_save);
701                 MEM_freeN(frontbuffer_save);
702                 frontbuffer_save= NULL;
703         }
704 }
705
706 void my_get_frontbuffer_image(int x, int y, int sx, int sy)
707 {
708         if(frontbuffer_save) return;
709
710         ov_x= x;
711         ov_y= y;
712         ov_sx= sx;
713         ov_sy= sy;
714         
715         if(sx>1 && sy>1) {
716                 frontbuffer_save= MEM_mallocN(sx*sy*4, "temp_frontbuffer_image");
717                 glReadPixels(x, y, sx, sy, GL_RGBA, GL_UNSIGNED_BYTE, frontbuffer_save);
718         }
719
720         #ifdef WIN32
721         /* different coord system! */
722         y= (G.curscreen->sizey-y);
723         
724         if(curswin>3) {
725                 y -= curarea->winrct.ymin;
726         }
727         #endif
728 }
729
730 int mywin_inmenu(void) {
731         return frontbuffer_save?1:0;
732 }
733
734 void mywin_getmenu_rect(int *x, int *y, int *sx, int *sy) {
735         *x= ov_x;
736         *sx= ov_sx;
737         *sy= ov_sy;
738
739 #if defined(WIN32) || defined (__BeOS)
740         *y= ov_y;
741 #else
742         *y= (G.curscreen->sizey - ov_y) - ov_sy;
743 #endif  
744 }