Fix T45477 wrong edge selection.
[blender.git] / source / blender / windowmanager / intern / wm_subwindow.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * Contributor(s): 2007 Blender Foundation (refactor)
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  *
25  *
26  * Subwindow opengl handling. 
27  * BTW: subwindows open/close in X11 are way too slow, tried it, and choose for my own system... (ton)
28  * 
29  */
30
31 /** \file blender/windowmanager/intern/wm_subwindow.c
32  *  \ingroup wm
33  *
34  * Internal subwindows used for OpenGL state, used for regions and screens.
35  */
36
37 #include <string.h>
38
39 #include "MEM_guardedalloc.h"
40
41 #include "DNA_windowmanager_types.h"
42 #include "DNA_screen_types.h"
43
44 #include "BLI_blenlib.h"
45 #include "BLI_math.h"
46 #include "BLI_utildefines.h"
47
48 #include "BIF_gl.h"
49
50 #include "GPU_extensions.h"
51
52 #include "WM_api.h"
53 #include "wm_subwindow.h"
54
55 /**
56  * \note #wmSubWindow stored in #wmWindow but not exposed outside this C file,
57  * it seems a bit redundant (area regions can store it too, but we keep it
58  * because we can store all kind of future opengl fanciness here.
59  *
60  * We use indices and array because:
61  * - index has safety, no pointers from this C file hanging around
62  * - fast lookups of indices with array, list would give overhead
63  * - old code used it this way...
64  * - keep option open to have 2 screens using same window
65  */
66
67 typedef struct wmSubWindow {
68         struct wmSubWindow *next, *prev;
69         
70         rcti winrct;
71         int swinid;
72 } wmSubWindow;
73
74
75 /* ******************* open, free, set, get data ******************** */
76
77 /* not subwindow itself */
78 static void wm_subwindow_free(wmSubWindow *UNUSED(swin))
79 {
80         /* future fancy stuff */
81 }
82
83 void wm_subwindows_free(wmWindow *win)
84 {
85         wmSubWindow *swin;
86         
87         for (swin = win->subwindows.first; swin; swin = swin->next)
88                 wm_subwindow_free(swin);
89         
90         BLI_freelistN(&win->subwindows);
91 }
92
93
94 int wm_subwindow_get_id(wmWindow *win)
95 {
96         if (win->curswin)
97                 return win->curswin->swinid;
98         return 0;
99 }
100
101 static wmSubWindow *swin_from_swinid(wmWindow *win, int swinid)
102 {
103         wmSubWindow *swin;
104         
105         for (swin = win->subwindows.first; swin; swin = swin->next)
106                 if (swin->swinid == swinid)
107                         break;
108         return swin;
109 }
110
111
112 static void wm_swin_size_get(wmSubWindow *swin, int *x, int *y)
113 {
114         *x = BLI_rcti_size_x(&swin->winrct) + 1;
115         *y = BLI_rcti_size_y(&swin->winrct) + 1;
116 }
117 void wm_subwindow_size_get(wmWindow *win, int swinid, int *x, int *y)
118 {
119         wmSubWindow *swin = swin_from_swinid(win, swinid);
120
121         if (swin) {
122                 wm_swin_size_get(swin, x, y);
123         }
124 }
125
126
127 static void wm_swin_origin_get(wmSubWindow *swin, int *x, int *y)
128 {
129         *x = swin->winrct.xmin;
130         *y = swin->winrct.ymin;
131 }
132 void wm_subwindow_origin_get(wmWindow *win, int swinid, int *x, int *y)
133 {
134         wmSubWindow *swin = swin_from_swinid(win, swinid);
135
136         if (swin) {
137                 wm_swin_origin_get(swin, x, y);
138         }
139 }
140
141
142 static void wm_swin_matrix_get(wmWindow *win, wmSubWindow *swin, float mat[4][4])
143 {
144         /* used by UI, should find a better way to get the matrix there */
145         if (swin->swinid == win->screen->mainwin) {
146                 int width, height;
147
148                 wm_swin_size_get(swin, &width, &height);
149                 orthographic_m4(mat, -GLA_PIXEL_OFS, (float)width - GLA_PIXEL_OFS, -GLA_PIXEL_OFS, (float)height - GLA_PIXEL_OFS, -100, 100);
150         }
151         else {
152                 glGetFloatv(GL_PROJECTION_MATRIX, (float *)mat);
153         }
154 }
155 void wm_subwindow_matrix_get(wmWindow *win, int swinid, float mat[4][4])
156 {
157         wmSubWindow *swin = swin_from_swinid(win, swinid);
158
159         if (swin) {
160                 wm_swin_matrix_get(win, swin, mat);
161         }
162 }
163
164
165 static void wm_swin_rect_get(wmSubWindow *swin, rcti *r_rect)
166 {
167         *r_rect = swin->winrct;
168 }
169 void wm_subwindow_rect_get(wmWindow *win, int swinid, rcti *r_rect)
170 {
171         wmSubWindow *swin = swin_from_swinid(win, swinid);
172
173         if (swin) {
174                 wm_swin_rect_get(swin, r_rect);
175         }
176 }
177
178
179 static void wm_swin_rect_set(wmSubWindow *swin, const rcti *rect)
180 {
181         swin->winrct = *rect;
182 }
183 void wm_subwindow_rect_set(wmWindow *win, int swinid, const rcti *rect)
184 {
185         wmSubWindow *swin = swin_from_swinid(win, swinid);
186
187         if (swin) {
188                 wm_swin_rect_set(swin, rect);
189         }
190 }
191
192
193 /* always sets pixel-precise 2D window/view matrices */
194 /* coords is in whole pixels. xmin = 15, xmax = 16: means window is 2 pix big */
195 int wm_subwindow_open(wmWindow *win, const rcti *winrct)
196 {
197         wmSubWindow *swin;
198         int width, height;
199         int freewinid = 1;
200         
201         for (swin = win->subwindows.first; swin; swin = swin->next)
202                 if (freewinid <= swin->swinid)
203                         freewinid = swin->swinid + 1;
204
205         win->curswin = swin = MEM_callocN(sizeof(wmSubWindow), "swinopen");
206         BLI_addtail(&win->subwindows, swin);
207         
208         swin->swinid = freewinid;
209         swin->winrct = *winrct;
210
211         /* and we appy it all right away */
212         wmSubWindowSet(win, swin->swinid);
213         
214         /* extra service */
215         wm_swin_size_get(swin, &width, &height);
216         wmOrtho2_pixelspace(width, height);
217         glLoadIdentity();
218
219         return swin->swinid;
220 }
221
222
223 void wm_subwindow_close(wmWindow *win, int swinid)
224 {
225         wmSubWindow *swin = swin_from_swinid(win, swinid);
226
227         if (swin) {
228                 if (swin == win->curswin)
229                         win->curswin = NULL;
230                 wm_subwindow_free(swin);
231                 BLI_remlink(&win->subwindows, swin);
232                 MEM_freeN(swin);
233         }
234         else {
235                 printf("%s: Internal error, bad winid: %d\n", __func__, swinid);
236         }
237 }
238
239 /* pixels go from 0-99 for a 100 pixel window */
240 void wm_subwindow_position(wmWindow *win, int swinid, const rcti *winrct)
241 {
242         wmSubWindow *swin = swin_from_swinid(win, swinid);
243         
244         if (swin) {
245                 const int winsize_x = WM_window_pixels_x(win);
246                 const int winsize_y = WM_window_pixels_y(win);
247
248                 int width, height;
249                 
250                 swin->winrct = *winrct;
251                 
252                 /* CRITICAL, this clamping ensures that
253                  * the viewport never goes outside the screen
254                  * edges (assuming the x, y coords aren't
255                  *        outside). This caused a hardware lock
256                  * on Matrox cards if it happens.
257                  *
258                  * Really Blender should never _ever_ try
259                  * to do such a thing, but just to be safe
260                  * clamp it anyway (or fix the bScreen
261                  * scaling routine, and be damn sure you
262                  * fixed it). - zr  (2001!)
263                  */
264                 
265                 if (swin->winrct.xmax > winsize_x)
266                         swin->winrct.xmax = winsize_x;
267                 if (swin->winrct.ymax > winsize_y)
268                         swin->winrct.ymax = winsize_y;
269                 
270                 /* extra service */
271                 wmSubWindowSet(win, swinid);
272                 wm_swin_size_get(swin, &width, &height);
273                 wmOrtho2_pixelspace(width, height);
274         }
275         else {
276                 printf("%s: Internal error, bad winid: %d\n", __func__, swinid);
277         }
278 }
279
280 /* ---------------- WM versions of OpenGL style API calls ------------------------ */
281 /* ----------------- exported in WM_api.h ------------------------------------------------------ */
282
283 /* internal state, no threaded opengl! XXX */
284 static wmWindow *_curwindow = NULL;
285 static wmSubWindow *_curswin = NULL;
286
287 void wmSubWindowScissorSet(wmWindow *win, int swinid, const rcti *srct, bool srct_pad)
288 {
289         int width, height;
290         _curswin = swin_from_swinid(win, swinid);
291         
292         if (_curswin == NULL) {
293                 printf("%s %d: doesn't exist\n", __func__, swinid);
294                 return;
295         }
296         
297         win->curswin = _curswin;
298         _curwindow = win;
299         
300         width  = BLI_rcti_size_x(&_curswin->winrct) + 1;
301         height = BLI_rcti_size_y(&_curswin->winrct) + 1;
302         glViewport(_curswin->winrct.xmin, _curswin->winrct.ymin, width, height);
303         
304         if (srct) {
305                 int scissor_width  = BLI_rcti_size_x(srct);
306                 int scissor_height = BLI_rcti_size_y(srct);
307
308                 /* typically a single pixel doesn't matter,
309                  * but one pixel offset is noticeable with viewport border render */
310                 if (srct_pad) {
311                         scissor_width  += 1;
312                         scissor_height += 1;
313                 }
314
315                 glScissor(srct->xmin, srct->ymin, scissor_width, scissor_height);
316         }
317         else
318                 glScissor(_curswin->winrct.xmin, _curswin->winrct.ymin, width, height);
319         
320         wmOrtho2_pixelspace(width, height);
321         glLoadIdentity();
322         
323         glFlush();
324 }
325
326 /* enable the WM versions of opengl calls */
327 void wmSubWindowSet(wmWindow *win, int swinid)
328 {
329         wmSubWindowScissorSet(win, swinid, NULL, true);
330 }
331
332 void wmFrustum(float x1, float x2, float y1, float y2, float n, float f)
333 {
334         glMatrixMode(GL_PROJECTION);
335         glLoadIdentity();
336         glFrustum(x1, x2, y1, y2, n, f);
337         glMatrixMode(GL_MODELVIEW);
338 }
339
340 void wmOrtho(float x1, float x2, float y1, float y2, float n, float f)
341 {
342         glMatrixMode(GL_PROJECTION);
343         glLoadIdentity();
344
345         glOrtho(x1, x2, y1, y2, n, f);
346
347         glMatrixMode(GL_MODELVIEW);
348 }
349
350 void wmOrtho2(float x1, float x2, float y1, float y2)
351 {
352         /* prevent opengl from generating errors */
353         if (x1 == x2) x2 += 1.0f;
354         if (y1 == y2) y2 += 1.0f;
355
356         wmOrtho(x1, x2, y1, y2, -100, 100);
357 }
358
359 static void wmOrtho2_offset(const float x, const float y, const float ofs)
360 {
361         wmOrtho2(ofs, x + ofs, ofs, y + ofs);
362 }
363
364 /**
365  * default pixel alignment.
366  */
367 void wmOrtho2_region_pixelspace(const struct ARegion *ar)
368 {
369         wmOrtho2_offset(ar->winx, ar->winy, -0.01f);
370 }
371
372 void wmOrtho2_pixelspace(const float x, const float y)
373 {
374         wmOrtho2_offset(x, y, -GLA_PIXEL_OFS);
375 }
376
377 /**
378  * use for drawing uiBlock, any UI elements and text.
379  * \note prevents blurry text with multi-sample (FSAA), see T41749
380  */
381 void wmOrtho2_region_ui(const ARegion *ar)
382 {
383         /* note, intentionally no '+ 1',
384          * as with wmOrtho2_region_pixelspace */
385         wmOrtho2_offset(ar->winx, ar->winy, -0.01f);
386 }
387
388 /* *************************** Framebuffer color depth, for selection codes ********************** */
389
390 #ifdef __APPLE__
391
392 /* apple seems to round colors to below and up on some configs */
393
394 unsigned int index_to_framebuffer(int index)
395 {
396         unsigned int i = index;
397
398         switch (GPU_color_depth()) {
399                 case 12:
400                         i = ((i & 0xF00) << 12) + ((i & 0xF0) << 8) + ((i & 0xF) << 4);
401                         /* sometimes dithering subtracts! */
402                         i |= 0x070707;
403                         break;
404                 case 15:
405                 case 16:
406                         i = ((i & 0x7C00) << 9) + ((i & 0x3E0) << 6) + ((i & 0x1F) << 3);
407                         i |= 0x030303;
408                         break;
409                 case 24:
410                         break;
411                 default: /* 18 bits... */
412                         i = ((i & 0x3F000) << 6) + ((i & 0xFC0) << 4) + ((i & 0x3F) << 2);
413                         i |= 0x010101;
414                         break;
415         }
416
417         return i;
418 }
419
420 #else
421
422 /* this is the old method as being in use for ages.... seems to work? colors are rounded to lower values */
423
424 unsigned int index_to_framebuffer(int index)
425 {
426         unsigned int i = index;
427         
428         switch (GPU_color_depth()) {
429                 case 8:
430                         i = ((i & 48) << 18) + ((i & 12) << 12) + ((i & 3) << 6);
431                         i |= 0x3F3F3F;
432                         break;
433                 case 12:
434                         i = ((i & 0xF00) << 12) + ((i & 0xF0) << 8) + ((i & 0xF) << 4);
435                         /* sometimes dithering subtracts! */
436                         i |= 0x0F0F0F;
437                         break;
438                 case 15:
439                 case 16:
440                         i = ((i & 0x7C00) << 9) + ((i & 0x3E0) << 6) + ((i & 0x1F) << 3);
441                         i |= 0x070707;
442                         break;
443                 case 24:
444                         break;
445                 default:    /* 18 bits... */
446                         i = ((i & 0x3F000) << 6) + ((i & 0xFC0) << 4) + ((i & 0x3F) << 2);
447                         i |= 0x030303;
448                         break;
449         }
450         
451         return i;
452 }
453
454 #endif
455
456 void WM_framebuffer_index_set(int index)
457 {
458         const int col = index_to_framebuffer(index);
459         cpack(col);
460 }
461
462 void WM_framebuffer_index_get(int index, int *r_col)
463 {
464         const int col = index_to_framebuffer(index);
465         *r_col = ((col & 0xFF) << 24) | /* red */
466                  (((col >>  8) & 0xFF) << 16) | /* green */
467                  (((col >> 16) & 0xFF) << 8) | /* blue */
468                  0xFF; /* alpha */
469 }
470
471
472
473 #define INDEX_FROM_BUF_8(col)     (((col & 0xC00000) >> 18) + ((col & 0xC000) >> 12) + ((col & 0xC0) >> 6))
474 #define INDEX_FROM_BUF_12(col)    (((col & 0xF00000) >> 12) + ((col & 0xF000) >> 8)  + ((col & 0xF0) >> 4))
475 #define INDEX_FROM_BUF_15_16(col) (((col & 0xF80000) >> 9)  + ((col & 0xF800) >> 6)  + ((col & 0xF8) >> 3))
476 #define INDEX_FROM_BUF_18(col)    (((col & 0xFC0000) >> 6)  + ((col & 0xFC00) >> 4)  + ((col & 0xFC) >> 2))
477 #define INDEX_FROM_BUF_24(col)      (col & 0xFFFFFF)
478
479 int WM_framebuffer_to_index(unsigned int col)
480 {
481         if (col == 0) {
482                 return 0;
483         }
484
485         switch (GPU_color_depth()) {
486                 case  8: return INDEX_FROM_BUF_8(col);
487                 case 12: return INDEX_FROM_BUF_12(col);
488                 case 15:
489                 case 16: return INDEX_FROM_BUF_15_16(col);
490                 case 24: return INDEX_FROM_BUF_24(col);
491                 default: return INDEX_FROM_BUF_18(col);
492         }
493 }
494
495 void WM_framebuffer_to_index_array(unsigned int *col, const unsigned int size)
496 {
497 #define INDEX_BUF_ARRAY(INDEX_FROM_BUF_BITS) \
498         for (i = size; i--; col++) { \
499                 if ((c = *col)) { \
500                         *col = INDEX_FROM_BUF_BITS(c); \
501                 } \
502         } ((void)0)
503
504         if (size > 0) {
505                 unsigned int i, c;
506
507                 switch (GPU_color_depth()) {
508                         case  8:
509                                 INDEX_BUF_ARRAY(INDEX_FROM_BUF_8);
510                                 break;
511                         case 12:
512                                 INDEX_BUF_ARRAY(INDEX_FROM_BUF_12);
513                                 break;
514                         case 15:
515                         case 16:
516                                 INDEX_BUF_ARRAY(INDEX_FROM_BUF_15_16);
517                                 break;
518                         case 24:
519                                 INDEX_BUF_ARRAY(INDEX_FROM_BUF_24);
520                                 break;
521                         default:
522                                 INDEX_BUF_ARRAY(INDEX_FROM_BUF_18);
523                                 break;
524                 }
525         }
526
527 #undef INDEX_BUF_ARRAY
528 }
529
530
531 /* ********** END MY WINDOW ************** */
532