1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup edscr
22  */
23 
24 #include <math.h>
25 #include <string.h>
26 
27 #include "MEM_guardedalloc.h"
28 
29 #include "DNA_object_types.h"
30 #include "DNA_scene_types.h"
31 #include "DNA_userdef_types.h"
32 #include "DNA_workspace_types.h"
33 
34 #include "BLI_blenlib.h"
35 #include "BLI_utildefines.h"
36 
37 #include "BKE_context.h"
38 #include "BKE_global.h"
39 #include "BKE_icons.h"
40 #include "BKE_image.h"
41 #include "BKE_layer.h"
42 #include "BKE_lib_id.h"
43 #include "BKE_main.h"
44 #include "BKE_scene.h"
45 #include "BKE_screen.h"
46 #include "BKE_sound.h"
47 #include "BKE_workspace.h"
48 
49 #include "WM_api.h"
50 #include "WM_types.h"
51 
52 #include "ED_clip.h"
53 #include "ED_node.h"
54 #include "ED_screen.h"
55 #include "ED_screen_types.h"
56 
57 #include "UI_interface.h"
58 
59 #include "WM_message.h"
60 
61 #include "DEG_depsgraph_query.h"
62 
63 #include "screen_intern.h" /* own module include */
64 
65 /* adds no space data */
screen_addarea_ex(ScrAreaMap * area_map,ScrVert * bottom_left,ScrVert * top_left,ScrVert * top_right,ScrVert * bottom_right,short spacetype)66 static ScrArea *screen_addarea_ex(ScrAreaMap *area_map,
67                                   ScrVert *bottom_left,
68                                   ScrVert *top_left,
69                                   ScrVert *top_right,
70                                   ScrVert *bottom_right,
71                                   short spacetype)
72 {
73   ScrArea *area = MEM_callocN(sizeof(ScrArea), "addscrarea");
74 
75   area->v1 = bottom_left;
76   area->v2 = top_left;
77   area->v3 = top_right;
78   area->v4 = bottom_right;
79   area->spacetype = spacetype;
80 
81   BLI_addtail(&area_map->areabase, area);
82 
83   return area;
84 }
screen_addarea(bScreen * screen,ScrVert * left_bottom,ScrVert * left_top,ScrVert * right_top,ScrVert * right_bottom,short spacetype)85 static ScrArea *screen_addarea(bScreen *screen,
86                                ScrVert *left_bottom,
87                                ScrVert *left_top,
88                                ScrVert *right_top,
89                                ScrVert *right_bottom,
90                                short spacetype)
91 {
92   return screen_addarea_ex(
93       AREAMAP_FROM_SCREEN(screen), left_bottom, left_top, right_top, right_bottom, spacetype);
94 }
95 
screen_delarea(bContext * C,bScreen * screen,ScrArea * area)96 static void screen_delarea(bContext *C, bScreen *screen, ScrArea *area)
97 {
98 
99   ED_area_exit(C, area);
100 
101   BKE_screen_area_free(area);
102 
103   BLI_remlink(&screen->areabase, area);
104   MEM_freeN(area);
105 }
106 
area_split(const wmWindow * win,bScreen * screen,ScrArea * area,char dir,float fac,int merge)107 ScrArea *area_split(
108     const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge)
109 {
110   ScrArea *newa = NULL;
111 
112   if (area == NULL) {
113     return NULL;
114   }
115 
116   rcti window_rect;
117   WM_window_rect_calc(win, &window_rect);
118 
119   short split = screen_geom_find_area_split_point(area, &window_rect, dir, fac);
120   if (split == 0) {
121     return NULL;
122   }
123 
124   /* note regarding (fac > 0.5f) checks below.
125    * normally it shouldn't matter which is used since the copy should match the original
126    * however with viewport rendering and python console this isn't the case. - campbell */
127 
128   if (dir == 'h') {
129     /* new vertices */
130     ScrVert *sv1 = screen_geom_vertex_add(screen, area->v1->vec.x, split);
131     ScrVert *sv2 = screen_geom_vertex_add(screen, area->v4->vec.x, split);
132 
133     /* new edges */
134     screen_geom_edge_add(screen, area->v1, sv1);
135     screen_geom_edge_add(screen, sv1, area->v2);
136     screen_geom_edge_add(screen, area->v3, sv2);
137     screen_geom_edge_add(screen, sv2, area->v4);
138     screen_geom_edge_add(screen, sv1, sv2);
139 
140     if (fac > 0.5f) {
141       /* new areas: top */
142       newa = screen_addarea(screen, sv1, area->v2, area->v3, sv2, area->spacetype);
143 
144       /* area below */
145       area->v2 = sv1;
146       area->v3 = sv2;
147     }
148     else {
149       /* new areas: bottom */
150       newa = screen_addarea(screen, area->v1, sv1, sv2, area->v4, area->spacetype);
151 
152       /* area above */
153       area->v1 = sv1;
154       area->v4 = sv2;
155     }
156 
157     ED_area_data_copy(newa, area, true);
158   }
159   else {
160     /* new vertices */
161     ScrVert *sv1 = screen_geom_vertex_add(screen, split, area->v1->vec.y);
162     ScrVert *sv2 = screen_geom_vertex_add(screen, split, area->v2->vec.y);
163 
164     /* new edges */
165     screen_geom_edge_add(screen, area->v1, sv1);
166     screen_geom_edge_add(screen, sv1, area->v4);
167     screen_geom_edge_add(screen, area->v2, sv2);
168     screen_geom_edge_add(screen, sv2, area->v3);
169     screen_geom_edge_add(screen, sv1, sv2);
170 
171     if (fac > 0.5f) {
172       /* new areas: right */
173       newa = screen_addarea(screen, sv1, sv2, area->v3, area->v4, area->spacetype);
174 
175       /* area left */
176       area->v3 = sv2;
177       area->v4 = sv1;
178     }
179     else {
180       /* new areas: left */
181       newa = screen_addarea(screen, area->v1, area->v2, sv2, sv1, area->spacetype);
182 
183       /* area right */
184       area->v1 = sv1;
185       area->v2 = sv2;
186     }
187 
188     ED_area_data_copy(newa, area, true);
189   }
190 
191   /* remove double vertices en edges */
192   if (merge) {
193     BKE_screen_remove_double_scrverts(screen);
194   }
195   BKE_screen_remove_double_scredges(screen);
196   BKE_screen_remove_unused_scredges(screen);
197 
198   return newa;
199 }
200 
201 /**
202  * Empty screen, with 1 dummy area without spacedata. Uses window size.
203  */
screen_add(Main * bmain,const char * name,const rcti * rect)204 bScreen *screen_add(Main *bmain, const char *name, const rcti *rect)
205 {
206   bScreen *screen = BKE_libblock_alloc(bmain, ID_SCR, name, 0);
207   screen->do_refresh = true;
208   screen->redraws_flag = TIME_ALL_3D_WIN | TIME_ALL_ANIM_WIN;
209 
210   ScrVert *sv1 = screen_geom_vertex_add(screen, rect->xmin, rect->ymin);
211   ScrVert *sv2 = screen_geom_vertex_add(screen, rect->xmin, rect->ymax - 1);
212   ScrVert *sv3 = screen_geom_vertex_add(screen, rect->xmax - 1, rect->ymax - 1);
213   ScrVert *sv4 = screen_geom_vertex_add(screen, rect->xmax - 1, rect->ymin);
214 
215   screen_geom_edge_add(screen, sv1, sv2);
216   screen_geom_edge_add(screen, sv2, sv3);
217   screen_geom_edge_add(screen, sv3, sv4);
218   screen_geom_edge_add(screen, sv4, sv1);
219 
220   /* dummy type, no spacedata */
221   screen_addarea(screen, sv1, sv2, sv3, sv4, SPACE_EMPTY);
222 
223   return screen;
224 }
225 
screen_data_copy(bScreen * to,bScreen * from)226 void screen_data_copy(bScreen *to, bScreen *from)
227 {
228   /* free contents of 'to', is from blenkernel screen.c */
229   BKE_screen_free(to);
230 
231   to->flag = from->flag;
232 
233   BLI_duplicatelist(&to->vertbase, &from->vertbase);
234   BLI_duplicatelist(&to->edgebase, &from->edgebase);
235   BLI_duplicatelist(&to->areabase, &from->areabase);
236   BLI_listbase_clear(&to->regionbase);
237 
238   ScrVert *s2 = to->vertbase.first;
239   for (ScrVert *s1 = from->vertbase.first; s1; s1 = s1->next, s2 = s2->next) {
240     s1->newv = s2;
241   }
242 
243   LISTBASE_FOREACH (ScrEdge *, se, &to->edgebase) {
244     se->v1 = se->v1->newv;
245     se->v2 = se->v2->newv;
246     BKE_screen_sort_scrvert(&(se->v1), &(se->v2));
247   }
248 
249   ScrArea *from_area = from->areabase.first;
250   LISTBASE_FOREACH (ScrArea *, area, &to->areabase) {
251     area->v1 = area->v1->newv;
252     area->v2 = area->v2->newv;
253     area->v3 = area->v3->newv;
254     area->v4 = area->v4->newv;
255 
256     BLI_listbase_clear(&area->spacedata);
257     BLI_listbase_clear(&area->regionbase);
258     BLI_listbase_clear(&area->actionzones);
259     BLI_listbase_clear(&area->handlers);
260 
261     ED_area_data_copy(area, from_area, true);
262 
263     from_area = from_area->next;
264   }
265 
266   /* put at zero (needed?) */
267   LISTBASE_FOREACH (ScrVert *, s1, &from->vertbase) {
268     s1->newv = NULL;
269   }
270 }
271 
272 /**
273  * Prepare a newly created screen for initializing it as active screen.
274  */
screen_new_activate_prepare(const wmWindow * win,bScreen * screen_new)275 void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new)
276 {
277   screen_new->winid = win->winid;
278   screen_new->do_refresh = true;
279   screen_new->do_draw = true;
280 }
281 
282 /* with area as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
283 /* -1 = not valid check */
284 /* used with join operator */
area_getorientation(ScrArea * area,ScrArea * sb)285 int area_getorientation(ScrArea *area, ScrArea *sb)
286 {
287   if (area == NULL || sb == NULL) {
288     return -1;
289   }
290 
291   ScrVert *saBL = area->v1;
292   ScrVert *saTL = area->v2;
293   ScrVert *saTR = area->v3;
294   ScrVert *saBR = area->v4;
295 
296   ScrVert *sbBL = sb->v1;
297   ScrVert *sbTL = sb->v2;
298   ScrVert *sbTR = sb->v3;
299   ScrVert *sbBR = sb->v4;
300 
301   if (saBL->vec.x == sbBR->vec.x && saTL->vec.x == sbTR->vec.x) { /* area to right of sb = W */
302     if ((abs(saBL->vec.y - sbBR->vec.y) <= AREAJOINTOLERANCE) &&
303         (abs(saTL->vec.y - sbTR->vec.y) <= AREAJOINTOLERANCE)) {
304       return 0;
305     }
306   }
307   else if (saTL->vec.y == sbBL->vec.y &&
308            saTR->vec.y == sbBR->vec.y) { /* area to bottom of sb = N */
309     if ((abs(saTL->vec.x - sbBL->vec.x) <= AREAJOINTOLERANCE) &&
310         (abs(saTR->vec.x - sbBR->vec.x) <= AREAJOINTOLERANCE)) {
311       return 1;
312     }
313   }
314   else if (saTR->vec.x == sbTL->vec.x && saBR->vec.x == sbBL->vec.x) { /* area to left of sb = E */
315     if ((abs(saTR->vec.y - sbTL->vec.y) <= AREAJOINTOLERANCE) &&
316         (abs(saBR->vec.y - sbBL->vec.y) <= AREAJOINTOLERANCE)) {
317       return 2;
318     }
319   }
320   else if (saBL->vec.y == sbTL->vec.y && saBR->vec.y == sbTR->vec.y) { /* area on top of sb = S*/
321     if ((abs(saBL->vec.x - sbTL->vec.x) <= AREAJOINTOLERANCE) &&
322         (abs(saBR->vec.x - sbTR->vec.x) <= AREAJOINTOLERANCE)) {
323       return 3;
324     }
325   }
326 
327   return -1;
328 }
329 
330 /* Screen verts with horizontal position equal to from_x are moved to to_x. */
screen_verts_halign(const wmWindow * win,const bScreen * screen,const short from_x,const short to_x)331 static void screen_verts_halign(const wmWindow *win,
332                                 const bScreen *screen,
333                                 const short from_x,
334                                 const short to_x)
335 {
336   ED_screen_verts_iter(win, screen, v1)
337   {
338     if (v1->vec.x == from_x) {
339       v1->vec.x = to_x;
340     }
341   }
342 }
343 
344 /* Screen verts with vertical position equal to from_y are moved to to_y. */
screen_verts_valign(const wmWindow * win,const bScreen * screen,const short from_y,const short to_y)345 static void screen_verts_valign(const wmWindow *win,
346                                 const bScreen *screen,
347                                 const short from_y,
348                                 const short to_y)
349 {
350   ED_screen_verts_iter(win, screen, v1)
351   {
352     if (v1->vec.y == from_y) {
353       v1->vec.y = to_y;
354     }
355   }
356 }
357 
358 /* Adjust all screen edges to allow joining two areas. 'dir' value is like area_getorientation().
359  */
screen_areas_align(bContext * C,bScreen * screen,ScrArea * sa1,ScrArea * sa2,const int dir)360 static void screen_areas_align(
361     bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const int dir)
362 {
363   wmWindow *win = CTX_wm_window(C);
364 
365   if (dir == 0 || dir == 2) {
366     /* horizontal join, use average for new top and bottom. */
367     int top = (sa1->v2->vec.y + sa2->v2->vec.y) / 2;
368     int bottom = (sa1->v4->vec.y + sa2->v4->vec.y) / 2;
369 
370     /* Move edges exactly matching source top and bottom. */
371     screen_verts_valign(win, screen, sa1->v2->vec.y, top);
372     screen_verts_valign(win, screen, sa1->v4->vec.y, bottom);
373 
374     /* Move edges exactly matching target top and bottom. */
375     screen_verts_valign(win, screen, sa2->v2->vec.y, top);
376     screen_verts_valign(win, screen, sa2->v4->vec.y, bottom);
377   }
378   else {
379     /* Vertical join, use averages for new left and right. */
380     int left = (sa1->v1->vec.x + sa2->v1->vec.x) / 2;
381     int right = (sa1->v3->vec.x + sa2->v3->vec.x) / 2;
382 
383     /* Move edges exactly matching source left and right. */
384     screen_verts_halign(win, screen, sa1->v1->vec.x, left);
385     screen_verts_halign(win, screen, sa1->v3->vec.x, right);
386 
387     /* Move edges exactly matching target left and right */
388     screen_verts_halign(win, screen, sa2->v1->vec.x, left);
389     screen_verts_halign(win, screen, sa2->v3->vec.x, right);
390   }
391 }
392 
393 /* Helper function to join 2 areas, it has a return value, 0=failed 1=success
394  * used by the split, join operators
395  */
screen_area_join(bContext * C,bScreen * screen,ScrArea * sa1,ScrArea * sa2)396 int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
397 {
398   int dir = area_getorientation(sa1, sa2);
399 
400   if (dir == -1) {
401     return 0;
402   }
403 
404   /* Align areas if they are not. Do sanity checking before getting here. */
405   screen_areas_align(C, screen, sa1, sa2, dir);
406 
407   if (dir == 0) {      /* sa1 to right of sa2 = W */
408     sa1->v1 = sa2->v1; /* BL */
409     sa1->v2 = sa2->v2; /* TL */
410     screen_geom_edge_add(screen, sa1->v2, sa1->v3);
411     screen_geom_edge_add(screen, sa1->v1, sa1->v4);
412   }
413   else if (dir == 1) { /* sa1 to bottom of sa2 = N */
414     sa1->v2 = sa2->v2; /* TL */
415     sa1->v3 = sa2->v3; /* TR */
416     screen_geom_edge_add(screen, sa1->v1, sa1->v2);
417     screen_geom_edge_add(screen, sa1->v3, sa1->v4);
418   }
419   else if (dir == 2) { /* sa1 to left of sa2 = E */
420     sa1->v3 = sa2->v3; /* TR */
421     sa1->v4 = sa2->v4; /* BR */
422     screen_geom_edge_add(screen, sa1->v2, sa1->v3);
423     screen_geom_edge_add(screen, sa1->v1, sa1->v4);
424   }
425   else if (dir == 3) { /* sa1 on top of sa2 = S */
426     sa1->v1 = sa2->v1; /* BL */
427     sa1->v4 = sa2->v4; /* BR */
428     screen_geom_edge_add(screen, sa1->v1, sa1->v2);
429     screen_geom_edge_add(screen, sa1->v3, sa1->v4);
430   }
431 
432   screen_delarea(C, screen, sa2);
433   BKE_screen_remove_double_scrverts(screen);
434   /* Update preview thumbnail */
435   BKE_icon_changed(screen->id.icon_id);
436 
437   return 1;
438 }
439 
440 /* ****************** EXPORTED API TO OTHER MODULES *************************** */
441 
442 /* screen sets cursor based on active region */
region_cursor_set_ex(wmWindow * win,ScrArea * area,ARegion * region,bool swin_changed)443 static void region_cursor_set_ex(wmWindow *win, ScrArea *area, ARegion *region, bool swin_changed)
444 {
445   BLI_assert(WM_window_get_active_screen(win)->active_region == region);
446   if (win->tag_cursor_refresh || swin_changed || (region->type && region->type->event_cursor)) {
447     win->tag_cursor_refresh = false;
448     ED_region_cursor_set(win, area, region);
449   }
450 }
451 
region_cursor_set(wmWindow * win,bool swin_changed)452 static void region_cursor_set(wmWindow *win, bool swin_changed)
453 {
454   bScreen *screen = WM_window_get_active_screen(win);
455 
456   ED_screen_areas_iter (win, screen, area) {
457     LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
458       if (region == screen->active_region) {
459         region_cursor_set_ex(win, area, region, swin_changed);
460         return;
461       }
462     }
463   }
464 }
465 
ED_screen_do_listen(bContext * C,wmNotifier * note)466 void ED_screen_do_listen(bContext *C, wmNotifier *note)
467 {
468   wmWindow *win = CTX_wm_window(C);
469   bScreen *screen = CTX_wm_screen(C);
470 
471   /* generic notes */
472   switch (note->category) {
473     case NC_WM:
474       if (note->data == ND_FILEREAD) {
475         screen->do_draw = true;
476       }
477       break;
478     case NC_WINDOW:
479       screen->do_draw = true;
480       break;
481     case NC_SCREEN:
482       if (note->action == NA_EDITED) {
483         screen->do_draw = screen->do_refresh = true;
484       }
485       break;
486     case NC_SCENE:
487       if (note->data == ND_MODE) {
488         region_cursor_set(win, true);
489       }
490       break;
491   }
492 }
493 
494 /* make this screen usable */
495 /* for file read and first use, for scaling window, area moves */
ED_screen_refresh(wmWindowManager * wm,wmWindow * win)496 void ED_screen_refresh(wmWindowManager *wm, wmWindow *win)
497 {
498   bScreen *screen = WM_window_get_active_screen(win);
499 
500   /* exception for bg mode, we only need the screen context */
501   if (!G.background) {
502     /* header size depends on DPI, let's verify */
503     WM_window_set_dpi(win);
504 
505     ED_screen_global_areas_refresh(win);
506 
507     screen_geom_vertices_scale(win, screen);
508 
509     ED_screen_areas_iter (win, screen, area) {
510       /* set spacetype and region callbacks, calls init() */
511       /* sets subwindows for regions, adds handlers */
512       ED_area_init(wm, win, area);
513     }
514 
515     /* wake up animtimer */
516     if (screen->animtimer) {
517       WM_event_timer_sleep(wm, win, screen->animtimer, false);
518     }
519   }
520 
521   if (G.debug & G_DEBUG_EVENTS) {
522     printf("%s: set screen\n", __func__);
523   }
524   screen->do_refresh = false;
525   /* prevent multiwin errors */
526   screen->winid = win->winid;
527 
528   screen->context = ed_screen_context;
529 }
530 
531 /* file read, set all screens, ... */
ED_screens_init(Main * bmain,wmWindowManager * wm)532 void ED_screens_init(Main *bmain, wmWindowManager *wm)
533 {
534   LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
535     if (BKE_workspace_active_get(win->workspace_hook) == NULL) {
536       BKE_workspace_active_set(win->workspace_hook, bmain->workspaces.first);
537     }
538 
539     ED_screen_refresh(wm, win);
540     if (win->eventstate) {
541       ED_screen_set_active_region(NULL, win, &win->eventstate->x);
542     }
543   }
544 
545   if (U.uiflag & USER_HEADER_FROM_PREF) {
546     LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
547       BKE_screen_header_alignment_reset(screen);
548     }
549   }
550 }
551 
ED_screen_ensure_updated(wmWindowManager * wm,wmWindow * win,bScreen * screen)552 void ED_screen_ensure_updated(wmWindowManager *wm, wmWindow *win, bScreen *screen)
553 {
554   if (screen->do_refresh) {
555     ED_screen_refresh(wm, win);
556   }
557 }
558 
559 /**
560  * Utility to exit and free an area-region. Screen level regions (menus/popups) need to be treated
561  * slightly differently, see #ui_region_temp_remove().
562  */
ED_region_remove(bContext * C,ScrArea * area,ARegion * region)563 void ED_region_remove(bContext *C, ScrArea *area, ARegion *region)
564 {
565   ED_region_exit(C, region);
566   BKE_area_region_free(area->type, region);
567   BLI_freelinkN(&area->regionbase, region);
568 }
569 
570 /* *********** exit calls are for closing running stuff ******** */
571 
ED_region_exit(bContext * C,ARegion * region)572 void ED_region_exit(bContext *C, ARegion *region)
573 {
574   wmWindowManager *wm = CTX_wm_manager(C);
575   wmWindow *win = CTX_wm_window(C);
576   ARegion *prevar = CTX_wm_region(C);
577 
578   if (region->type && region->type->exit) {
579     region->type->exit(wm, region);
580   }
581 
582   CTX_wm_region_set(C, region);
583 
584   WM_event_remove_handlers(C, &region->handlers);
585   WM_event_modal_handler_region_replace(win, region, NULL);
586   WM_draw_region_free(region, true);
587 
588   if (region->headerstr) {
589     MEM_freeN(region->headerstr);
590     region->headerstr = NULL;
591   }
592 
593   if (region->regiontimer) {
594     WM_event_remove_timer(wm, win, region->regiontimer);
595     region->regiontimer = NULL;
596   }
597 
598   WM_msgbus_clear_by_owner(wm->message_bus, region);
599 
600   CTX_wm_region_set(C, prevar);
601 }
602 
ED_area_exit(bContext * C,ScrArea * area)603 void ED_area_exit(bContext *C, ScrArea *area)
604 {
605   wmWindowManager *wm = CTX_wm_manager(C);
606   wmWindow *win = CTX_wm_window(C);
607   ScrArea *prevsa = CTX_wm_area(C);
608 
609   if (area->type && area->type->exit) {
610     area->type->exit(wm, area);
611   }
612 
613   CTX_wm_area_set(C, area);
614 
615   LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
616     ED_region_exit(C, region);
617   }
618 
619   WM_event_remove_handlers(C, &area->handlers);
620   WM_event_modal_handler_area_replace(win, area, NULL);
621 
622   CTX_wm_area_set(C, prevsa);
623 }
624 
ED_screen_exit(bContext * C,wmWindow * window,bScreen * screen)625 void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen)
626 {
627   wmWindowManager *wm = CTX_wm_manager(C);
628   wmWindow *prevwin = CTX_wm_window(C);
629 
630   CTX_wm_window_set(C, window);
631 
632   if (screen->animtimer) {
633     WM_event_remove_timer(wm, window, screen->animtimer);
634 
635     Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
636     Scene *scene = WM_window_get_active_scene(prevwin);
637     Scene *scene_eval = (Scene *)DEG_get_evaluated_id(depsgraph, &scene->id);
638     BKE_sound_stop_scene(scene_eval);
639   }
640   screen->animtimer = NULL;
641   screen->scrubbing = false;
642 
643   screen->active_region = NULL;
644 
645   LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
646     ED_region_exit(C, region);
647   }
648   LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
649     ED_area_exit(C, area);
650   }
651   /* Don't use ED_screen_areas_iter here, it skips hidden areas. */
652   LISTBASE_FOREACH (ScrArea *, area, &window->global_areas.areabase) {
653     ED_area_exit(C, area);
654   }
655 
656   /* mark it available for use for other windows */
657   screen->winid = 0;
658 
659   if (!WM_window_is_temp_screen(prevwin)) {
660     /* use previous window if possible */
661     CTX_wm_window_set(C, prevwin);
662   }
663   else {
664     /* none otherwise */
665     CTX_wm_window_set(C, NULL);
666   }
667 }
668 
669 /* *********************************** */
670 
671 /* case when on area-edge or in azones, or outside window */
screen_cursor_set(wmWindow * win,const int xy[2])672 static void screen_cursor_set(wmWindow *win, const int xy[2])
673 {
674   const bScreen *screen = WM_window_get_active_screen(win);
675   AZone *az = NULL;
676   ScrArea *area = NULL;
677 
678   LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) {
679     if ((az = ED_area_actionzone_find_xy(area_iter, xy))) {
680       area = area_iter;
681       break;
682     }
683   }
684 
685   if (area) {
686     if (az->type == AZONE_AREA) {
687       WM_cursor_set(win, WM_CURSOR_EDIT);
688     }
689     else if (az->type == AZONE_REGION) {
690       if (az->edge == AE_LEFT_TO_TOPRIGHT || az->edge == AE_RIGHT_TO_TOPLEFT) {
691         WM_cursor_set(win, WM_CURSOR_X_MOVE);
692       }
693       else {
694         WM_cursor_set(win, WM_CURSOR_Y_MOVE);
695       }
696     }
697   }
698   else {
699     ScrEdge *actedge = screen_geom_find_active_scredge(win, screen, xy[0], xy[1]);
700 
701     if (actedge) {
702       if (screen_geom_edge_is_horizontal(actedge)) {
703         WM_cursor_set(win, WM_CURSOR_Y_MOVE);
704       }
705       else {
706         WM_cursor_set(win, WM_CURSOR_X_MOVE);
707       }
708     }
709     else {
710       WM_cursor_set(win, WM_CURSOR_DEFAULT);
711     }
712   }
713 }
714 
715 /**
716  * Called in wm_event_system.c. sets state vars in screen, cursors.
717  * event type is mouse move.
718  */
ED_screen_set_active_region(bContext * C,wmWindow * win,const int xy[2])719 void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2])
720 {
721   bScreen *screen = WM_window_get_active_screen(win);
722   if (screen == NULL) {
723     return;
724   }
725 
726   ScrArea *area = NULL;
727   ARegion *region_prev = screen->active_region;
728 
729   ED_screen_areas_iter (win, screen, area_iter) {
730     if (xy[0] > (area_iter->totrct.xmin + BORDERPADDING) &&
731         xy[0] < (area_iter->totrct.xmax - BORDERPADDING)) {
732       if (xy[1] > (area_iter->totrct.ymin + BORDERPADDING) &&
733           xy[1] < (area_iter->totrct.ymax - BORDERPADDING)) {
734         if (ED_area_azones_update(area_iter, xy) == NULL) {
735           area = area_iter;
736           break;
737         }
738       }
739     }
740   }
741   if (area) {
742     /* Make overlap active when mouse over. */
743     LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
744       if (ED_region_contains_xy(region, xy)) {
745         screen->active_region = region;
746         break;
747       }
748     }
749   }
750   else {
751     screen->active_region = NULL;
752   }
753 
754   /* Check for redraw headers. */
755   if (region_prev != screen->active_region) {
756 
757     ED_screen_areas_iter (win, screen, area_iter) {
758       bool do_draw = false;
759 
760       LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) {
761         /* Call old area's deactivate if assigned. */
762         if (region == region_prev && area_iter->type->deactivate) {
763           area_iter->type->deactivate(area_iter);
764         }
765 
766         if (region == region_prev && region != screen->active_region) {
767           wmGizmoMap *gzmap = region_prev->gizmo_map;
768           if (gzmap) {
769             if (WM_gizmo_highlight_set(gzmap, NULL)) {
770               ED_region_tag_redraw_no_rebuild(region_prev);
771             }
772           }
773         }
774 
775         if (region == region_prev || region == screen->active_region) {
776           do_draw = true;
777         }
778       }
779 
780       if (do_draw) {
781         LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) {
782           if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) {
783             ED_region_tag_redraw_no_rebuild(region);
784           }
785         }
786       }
787     }
788   }
789 
790   /* Cursors, for time being set always on edges,
791    * otherwise the active region doesn't switch. */
792   if (screen->active_region == NULL) {
793     screen_cursor_set(win, xy);
794   }
795   else {
796     /* Notifier invokes freeing the buttons... causing a bit too much redraws. */
797     region_cursor_set_ex(win, area, screen->active_region, region_prev != screen->active_region);
798 
799     if (region_prev != screen->active_region) {
800       /* This used to be a notifier, but needs to be done immediate
801        * because it can undo setting the right button as active due
802        * to delayed notifier handling. */
803       if (C) {
804         UI_screen_free_active_but(C, screen);
805       }
806     }
807   }
808 }
809 
ED_screen_area_active(const bContext * C)810 int ED_screen_area_active(const bContext *C)
811 {
812   wmWindow *win = CTX_wm_window(C);
813   bScreen *screen = CTX_wm_screen(C);
814   ScrArea *area = CTX_wm_area(C);
815 
816   if (win && screen && area) {
817     AZone *az = ED_area_actionzone_find_xy(area, &win->eventstate->x);
818 
819     if (az && az->type == AZONE_REGION) {
820       return 1;
821     }
822 
823     LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
824       if (region == screen->active_region) {
825         return 1;
826       }
827     }
828   }
829   return 0;
830 }
831 
832 /**
833  * Add an area and geometry (screen-edges and -vertices) for it to \a area_map,
834  * with coordinates/dimensions matching \a rect.
835  */
screen_area_create_with_geometry(ScrAreaMap * area_map,const rcti * rect,short spacetype)836 static ScrArea *screen_area_create_with_geometry(ScrAreaMap *area_map,
837                                                  const rcti *rect,
838                                                  short spacetype)
839 {
840   ScrVert *bottom_left = screen_geom_vertex_add_ex(area_map, rect->xmin, rect->ymin);
841   ScrVert *top_left = screen_geom_vertex_add_ex(area_map, rect->xmin, rect->ymax);
842   ScrVert *top_right = screen_geom_vertex_add_ex(area_map, rect->xmax, rect->ymax);
843   ScrVert *bottom_right = screen_geom_vertex_add_ex(area_map, rect->xmax, rect->ymin);
844 
845   screen_geom_edge_add_ex(area_map, bottom_left, top_left);
846   screen_geom_edge_add_ex(area_map, top_left, top_right);
847   screen_geom_edge_add_ex(area_map, top_right, bottom_right);
848   screen_geom_edge_add_ex(area_map, bottom_right, bottom_left);
849 
850   return screen_addarea_ex(area_map, bottom_left, top_left, top_right, bottom_right, spacetype);
851 }
852 
screen_area_set_geometry_rect(ScrArea * area,const rcti * rect)853 static void screen_area_set_geometry_rect(ScrArea *area, const rcti *rect)
854 {
855   area->v1->vec.x = rect->xmin;
856   area->v1->vec.y = rect->ymin;
857   area->v2->vec.x = rect->xmin;
858   area->v2->vec.y = rect->ymax;
859   area->v3->vec.x = rect->xmax;
860   area->v3->vec.y = rect->ymax;
861   area->v4->vec.x = rect->xmax;
862   area->v4->vec.y = rect->ymin;
863 }
864 
screen_global_area_refresh(wmWindow * win,bScreen * screen,eSpace_Type space_type,GlobalAreaAlign align,const rcti * rect,const short height_cur,const short height_min,const short height_max)865 static void screen_global_area_refresh(wmWindow *win,
866                                        bScreen *screen,
867                                        eSpace_Type space_type,
868                                        GlobalAreaAlign align,
869                                        const rcti *rect,
870                                        const short height_cur,
871                                        const short height_min,
872                                        const short height_max)
873 {
874   ScrArea *area = NULL;
875   LISTBASE_FOREACH (ScrArea *, area_iter, &win->global_areas.areabase) {
876     if (area_iter->spacetype == space_type) {
877       area = area_iter;
878       break;
879     }
880   }
881 
882   if (area) {
883     screen_area_set_geometry_rect(area, rect);
884   }
885   else {
886     area = screen_area_create_with_geometry(&win->global_areas, rect, space_type);
887     SpaceType *stype = BKE_spacetype_from_id(space_type);
888     SpaceLink *slink = stype->create(area, WM_window_get_active_scene(win));
889 
890     area->regionbase = slink->regionbase;
891 
892     BLI_addhead(&area->spacedata, slink);
893     BLI_listbase_clear(&slink->regionbase);
894 
895     /* Data specific to global areas. */
896     area->global = MEM_callocN(sizeof(*area->global), __func__);
897     area->global->size_max = height_max;
898     area->global->size_min = height_min;
899     area->global->align = align;
900   }
901 
902   if (area->global->cur_fixed_height != height_cur) {
903     /* Refresh layout if size changes. */
904     area->global->cur_fixed_height = height_cur;
905     screen->do_refresh = true;
906   }
907 }
908 
screen_global_header_size(void)909 static int screen_global_header_size(void)
910 {
911   return (int)ceilf(ED_area_headersize() / UI_DPI_FAC);
912 }
913 
screen_global_topbar_area_refresh(wmWindow * win,bScreen * screen)914 static void screen_global_topbar_area_refresh(wmWindow *win, bScreen *screen)
915 {
916   const short size = screen_global_header_size();
917   rcti rect;
918 
919   BLI_rcti_init(&rect, 0, WM_window_pixels_x(win) - 1, 0, WM_window_pixels_y(win) - 1);
920   rect.ymin = rect.ymax - size;
921 
922   screen_global_area_refresh(
923       win, screen, SPACE_TOPBAR, GLOBAL_AREA_ALIGN_TOP, &rect, size, size, size);
924 }
925 
screen_global_statusbar_area_refresh(wmWindow * win,bScreen * screen)926 static void screen_global_statusbar_area_refresh(wmWindow *win, bScreen *screen)
927 {
928   const short size_min = 1;
929   const short size_max = 0.8f * screen_global_header_size();
930   const short size = (screen->flag & SCREEN_COLLAPSE_STATUSBAR) ? size_min : size_max;
931   rcti rect;
932 
933   BLI_rcti_init(&rect, 0, WM_window_pixels_x(win) - 1, 0, WM_window_pixels_y(win) - 1);
934   rect.ymax = rect.ymin + size_max;
935 
936   screen_global_area_refresh(
937       win, screen, SPACE_STATUSBAR, GLOBAL_AREA_ALIGN_BOTTOM, &rect, size, size_min, size_max);
938 }
939 
ED_screen_global_areas_sync(wmWindow * win)940 void ED_screen_global_areas_sync(wmWindow *win)
941 {
942   /* Update screen flags from height in window, this is weak and perhaps
943    * global areas should just become part of the screen instead. */
944   bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
945 
946   screen->flag &= ~SCREEN_COLLAPSE_STATUSBAR;
947 
948   LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) {
949     if (area->global->cur_fixed_height == area->global->size_min) {
950       if (area->spacetype == SPACE_STATUSBAR) {
951         screen->flag |= SCREEN_COLLAPSE_STATUSBAR;
952       }
953     }
954   }
955 }
956 
ED_screen_global_areas_refresh(wmWindow * win)957 void ED_screen_global_areas_refresh(wmWindow *win)
958 {
959   /* Don't create global area for child and temporary windows. */
960   bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
961   if ((win->parent != NULL) || screen->temp) {
962     if (win->global_areas.areabase.first) {
963       screen->do_refresh = true;
964       BKE_screen_area_map_free(&win->global_areas);
965     }
966     return;
967   }
968 
969   screen_global_topbar_area_refresh(win, screen);
970   screen_global_statusbar_area_refresh(win, screen);
971 }
972 
973 /* -------------------------------------------------------------------- */
974 /* Screen changing */
975 
976 /**
977  * \return the screen to activate.
978  * \warning The returned screen may not always equal \a screen_new!
979  */
screen_change_prepare(bScreen * screen_old,bScreen * screen_new,Main * bmain,bContext * C,wmWindow * win)980 void screen_change_prepare(
981     bScreen *screen_old, bScreen *screen_new, Main *bmain, bContext *C, wmWindow *win)
982 {
983   UNUSED_VARS_NDEBUG(bmain);
984   BLI_assert(BLI_findindex(&bmain->screens, screen_new) != -1);
985 
986   if (screen_old != screen_new) {
987     wmTimer *wt = screen_old->animtimer;
988 
989     /* remove handlers referencing areas in old screen */
990     LISTBASE_FOREACH (ScrArea *, area, &screen_old->areabase) {
991       WM_event_remove_area_handler(&win->modalhandlers, area);
992     }
993 
994     /* we put timer to sleep, so screen_exit has to think there's no timer */
995     screen_old->animtimer = NULL;
996     if (wt) {
997       WM_event_timer_sleep(CTX_wm_manager(C), win, wt, true);
998     }
999     ED_screen_exit(C, win, screen_old);
1000 
1001     /* Same scene, "transfer" playback to new screen. */
1002     if (wt) {
1003       screen_new->animtimer = wt;
1004     }
1005   }
1006 }
1007 
screen_change_update(bContext * C,wmWindow * win,bScreen * screen)1008 void screen_change_update(bContext *C, wmWindow *win, bScreen *screen)
1009 {
1010   Scene *scene = WM_window_get_active_scene(win);
1011   WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook);
1012   WorkSpaceLayout *layout = BKE_workspace_layout_find(workspace, screen);
1013 
1014   CTX_wm_window_set(C, win); /* stores C->wm.screen... hrmf */
1015 
1016   ED_screen_refresh(CTX_wm_manager(C), win);
1017 
1018   BKE_screen_view3d_scene_sync(screen, scene); /* sync new screen with scene data */
1019   WM_event_add_notifier(C, NC_WINDOW, NULL);
1020   WM_event_add_notifier(C, NC_SCREEN | ND_LAYOUTSET, layout);
1021 
1022   /* makes button hilites work */
1023   WM_event_add_mousemove(win);
1024 }
1025 
1026 /**
1027  * \brief Change the active screen.
1028  *
1029  * Operator call, WM + Window + screen already existed before
1030  *
1031  * \warning Do NOT call in area/region queues!
1032  * \returns if screen changing was successful.
1033  */
ED_screen_change(bContext * C,bScreen * screen)1034 bool ED_screen_change(bContext *C, bScreen *screen)
1035 {
1036   Main *bmain = CTX_data_main(C);
1037   wmWindow *win = CTX_wm_window(C);
1038   WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook);
1039   WorkSpaceLayout *layout = BKE_workspace_layout_find(workspace, screen);
1040   bScreen *screen_old = CTX_wm_screen(C);
1041 
1042   /* Get the actual layout/screen to be activated (guaranteed to be unused, even if that means
1043    * having to duplicate an existing one). */
1044   WorkSpaceLayout *layout_new = ED_workspace_screen_change_ensure_unused_layout(
1045       bmain, workspace, layout, layout, win);
1046   bScreen *screen_new = BKE_workspace_layout_screen_get(layout_new);
1047 
1048   screen_change_prepare(screen_old, screen_new, bmain, C, win);
1049 
1050   if (screen_old != screen_new) {
1051     WM_window_set_active_screen(win, workspace, screen_new);
1052     screen_change_update(C, win, screen_new);
1053 
1054     return true;
1055   }
1056 
1057   return false;
1058 }
1059 
screen_set_3dview_camera(Scene * scene,ViewLayer * view_layer,ScrArea * area,View3D * v3d)1060 static void screen_set_3dview_camera(Scene *scene,
1061                                      ViewLayer *view_layer,
1062                                      ScrArea *area,
1063                                      View3D *v3d)
1064 {
1065   /* fix any cameras that are used in the 3d view but not in the scene */
1066   BKE_screen_view3d_sync(v3d, scene);
1067 
1068   if (!v3d->camera || !BKE_view_layer_base_find(view_layer, v3d->camera)) {
1069     v3d->camera = BKE_view_layer_camera_find(view_layer);
1070     // XXX if (screen == curscreen) handle_view3d_lock();
1071     if (!v3d->camera) {
1072       ListBase *regionbase;
1073 
1074       /* regionbase is in different place depending if space is active */
1075       if (v3d == area->spacedata.first) {
1076         regionbase = &area->regionbase;
1077       }
1078       else {
1079         regionbase = &v3d->regionbase;
1080       }
1081 
1082       LISTBASE_FOREACH (ARegion *, region, regionbase) {
1083         if (region->regiontype == RGN_TYPE_WINDOW) {
1084           RegionView3D *rv3d = region->regiondata;
1085           if (rv3d->persp == RV3D_CAMOB) {
1086             rv3d->persp = RV3D_PERSP;
1087           }
1088         }
1089       }
1090     }
1091   }
1092 }
1093 
ED_screen_scene_change(bContext * C,wmWindow * win,Scene * scene)1094 void ED_screen_scene_change(bContext *C, wmWindow *win, Scene *scene)
1095 {
1096 #if 0
1097   ViewLayer *view_layer_old = WM_window_get_active_view_layer(win);
1098 #endif
1099 
1100   /* Switch scene. */
1101   win->scene = scene;
1102   if (CTX_wm_window(C) == win) {
1103     CTX_data_scene_set(C, scene);
1104   }
1105 
1106   /* Ensure the view layer name is updated. */
1107   WM_window_ensure_active_view_layer(win);
1108   ViewLayer *view_layer = WM_window_get_active_view_layer(win);
1109 
1110 #if 0
1111   /* Mode Syncing. */
1112   if (view_layer_old) {
1113     WorkSpace *workspace = CTX_wm_workspace(C);
1114     Object *obact_new = OBACT(view_layer);
1115     UNUSED_VARS(obact_new);
1116     eObjectMode object_mode_old = workspace->object_mode;
1117     Object *obact_old = OBACT(view_layer_old);
1118     UNUSED_VARS(obact_old, object_mode_old);
1119   }
1120 #endif
1121 
1122   /* Update 3D view cameras. */
1123   const bScreen *screen = WM_window_get_active_screen(win);
1124   LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
1125     LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
1126       if (sl->spacetype == SPACE_VIEW3D) {
1127         View3D *v3d = (View3D *)sl;
1128         screen_set_3dview_camera(scene, view_layer, area, v3d);
1129       }
1130     }
1131   }
1132 }
1133 
ED_screen_full_newspace(bContext * C,ScrArea * area,int type)1134 ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type)
1135 {
1136   wmWindow *win = CTX_wm_window(C);
1137   ScrArea *newsa = NULL;
1138   SpaceLink *newsl;
1139 
1140   if (!area || area->full == NULL) {
1141     newsa = ED_screen_state_toggle(C, win, area, SCREENMAXIMIZED);
1142   }
1143 
1144   if (!newsa) {
1145     newsa = area;
1146   }
1147 
1148   BLI_assert(newsa);
1149   newsl = newsa->spacedata.first;
1150 
1151   /* Tag the active space before changing, so we can identify it when user wants to go back. */
1152   if ((newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) {
1153     newsl->link_flag |= SPACE_FLAG_TYPE_WAS_ACTIVE;
1154   }
1155 
1156   ED_area_newspace(C, newsa, type, newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY);
1157 
1158   return newsa;
1159 }
1160 
1161 /**
1162  * \a was_prev_temp for the case previous space was a temporary fullscreen as well
1163  */
ED_screen_full_prevspace(bContext * C,ScrArea * area)1164 void ED_screen_full_prevspace(bContext *C, ScrArea *area)
1165 {
1166   BLI_assert(area->full);
1167 
1168   if (area->flag & AREA_FLAG_STACKED_FULLSCREEN) {
1169     /* stacked fullscreen -> only go back to previous area and don't toggle out of fullscreen */
1170     ED_area_prevspace(C, area);
1171   }
1172   else {
1173     ED_screen_restore_temp_type(C, area);
1174   }
1175 }
1176 
ED_screen_restore_temp_type(bContext * C,ScrArea * area)1177 void ED_screen_restore_temp_type(bContext *C, ScrArea *area)
1178 {
1179   SpaceLink *sl = area->spacedata.first;
1180 
1181   /* In case nether functions below run. */
1182   ED_area_tag_redraw(area);
1183 
1184   if (sl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) {
1185     ED_area_prevspace(C, area);
1186   }
1187 
1188   if (area->full) {
1189     ED_screen_state_toggle(C, CTX_wm_window(C), area, SCREENMAXIMIZED);
1190   }
1191 }
1192 
1193 /* restore a screen / area back to default operation, after temp fullscreen modes */
ED_screen_full_restore(bContext * C,ScrArea * area)1194 void ED_screen_full_restore(bContext *C, ScrArea *area)
1195 {
1196   wmWindow *win = CTX_wm_window(C);
1197   SpaceLink *sl = area->spacedata.first;
1198   bScreen *screen = CTX_wm_screen(C);
1199   short state = (screen ? screen->state : SCREENMAXIMIZED);
1200 
1201   /* if fullscreen area has a temporary space (such as a file browser or fullscreen render
1202    * overlaid on top of an existing setup) then return to the previous space */
1203 
1204   if (sl->next) {
1205     if (sl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) {
1206       ED_screen_full_prevspace(C, area);
1207     }
1208     else {
1209       ED_screen_state_toggle(C, win, area, state);
1210     }
1211     /* warning: 'area' may be freed */
1212   }
1213   /* otherwise just tile the area again */
1214   else {
1215     ED_screen_state_toggle(C, win, area, state);
1216   }
1217 }
1218 
1219 /**
1220  * this function toggles: if area is maximized/full then the parent will be restored
1221  *
1222  * \warning \a area may be freed.
1223  */
ED_screen_state_toggle(bContext * C,wmWindow * win,ScrArea * area,const short state)1224 ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const short state)
1225 {
1226   Main *bmain = CTX_data_main(C);
1227   wmWindowManager *wm = CTX_wm_manager(C);
1228   WorkSpace *workspace = WM_window_get_active_workspace(win);
1229 
1230   if (area) {
1231     /* ensure we don't have a button active anymore, can crash when
1232      * switching screens with tooltip open because region and tooltip
1233      * are no longer in the same screen */
1234     LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
1235       UI_blocklist_free(C, &region->uiblocks);
1236 
1237       if (region->regiontimer) {
1238         WM_event_remove_timer(wm, NULL, region->regiontimer);
1239         region->regiontimer = NULL;
1240       }
1241     }
1242 
1243     /* prevent hanging status prints */
1244     ED_area_status_text(area, NULL);
1245     ED_workspace_status_text(C, NULL);
1246   }
1247   bScreen *screen;
1248   if (area && area->full) {
1249     WorkSpaceLayout *layout_old = WM_window_get_active_layout(win);
1250     /* restoring back to SCREENNORMAL */
1251     screen = area->full;                                   /* the old screen to restore */
1252     bScreen *oldscreen = WM_window_get_active_screen(win); /* the one disappearing */
1253 
1254     BLI_assert(BKE_workspace_layout_screen_get(layout_old) != screen);
1255     BLI_assert(BKE_workspace_layout_screen_get(layout_old)->state != SCREENNORMAL);
1256 
1257     screen->state = SCREENNORMAL;
1258     screen->flag = oldscreen->flag;
1259 
1260     /* find old area to restore from */
1261     ScrArea *fullsa = NULL;
1262     LISTBASE_FOREACH (ScrArea *, old, &screen->areabase) {
1263       /* area to restore from is always first */
1264       if (old->full && !fullsa) {
1265         fullsa = old;
1266       }
1267 
1268       /* clear full screen state */
1269       old->full = NULL;
1270     }
1271 
1272     area->full = NULL;
1273 
1274     if (fullsa == NULL) {
1275       if (G.debug & G_DEBUG) {
1276         printf("%s: something wrong in areafullscreen\n", __func__);
1277       }
1278       return NULL;
1279     }
1280 
1281     if (state == SCREENFULL) {
1282       /* unhide global areas */
1283       LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) {
1284         glob_area->global->flag &= ~GLOBAL_AREA_IS_HIDDEN;
1285       }
1286       /* restore the old side panels/header visibility */
1287       LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
1288         region->flag = region->flagfullscreen;
1289       }
1290     }
1291 
1292     ED_area_data_swap(fullsa, area);
1293 
1294     /* animtimer back */
1295     screen->animtimer = oldscreen->animtimer;
1296     oldscreen->animtimer = NULL;
1297 
1298     ED_screen_change(C, screen);
1299     ED_area_tag_refresh(fullsa);
1300 
1301     BKE_workspace_layout_remove(CTX_data_main(C), workspace, layout_old);
1302 
1303     /* After we've restored back to SCREENNORMAL, we have to wait with
1304      * screen handling as it uses the area coords which aren't updated yet.
1305      * Without doing so, the screen handling gets wrong area coords,
1306      * which in worst case can lead to crashes (see T43139) */
1307     screen->skip_handling = true;
1308   }
1309   else {
1310     /* change from SCREENNORMAL to new state */
1311     WorkSpaceLayout *layout_new;
1312     ScrArea *newa;
1313     char newname[MAX_ID_NAME - 2];
1314 
1315     BLI_assert(ELEM(state, SCREENMAXIMIZED, SCREENFULL));
1316 
1317     bScreen *oldscreen = WM_window_get_active_screen(win);
1318 
1319     oldscreen->state = state;
1320     BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "nonnormal");
1321 
1322     layout_new = ED_workspace_layout_add(bmain, workspace, win, newname);
1323 
1324     screen = BKE_workspace_layout_screen_get(layout_new);
1325     screen->state = state;
1326     screen->redraws_flag = oldscreen->redraws_flag;
1327     screen->temp = oldscreen->temp;
1328     screen->flag = oldscreen->flag;
1329 
1330     /* timer */
1331     screen->animtimer = oldscreen->animtimer;
1332     oldscreen->animtimer = NULL;
1333 
1334     /* use random area when we have no active one, e.g. when the
1335      * mouse is outside of the window and we open a file browser */
1336     if (!area || area->global) {
1337       area = oldscreen->areabase.first;
1338     }
1339 
1340     newa = (ScrArea *)screen->areabase.first;
1341 
1342     /* copy area */
1343     ED_area_data_swap(newa, area);
1344     newa->flag = area->flag; /* mostly for AREA_FLAG_WASFULLSCREEN */
1345 
1346     if (state == SCREENFULL) {
1347       /* temporarily hide global areas */
1348       LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) {
1349         glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN;
1350       }
1351       /* temporarily hide the side panels/header */
1352       LISTBASE_FOREACH (ARegion *, region, &newa->regionbase) {
1353         region->flagfullscreen = region->flag;
1354 
1355         if (ELEM(region->regiontype,
1356                  RGN_TYPE_UI,
1357                  RGN_TYPE_HEADER,
1358                  RGN_TYPE_TOOL_HEADER,
1359                  RGN_TYPE_FOOTER,
1360                  RGN_TYPE_TOOLS,
1361                  RGN_TYPE_NAV_BAR,
1362                  RGN_TYPE_EXECUTE)) {
1363           region->flag |= RGN_FLAG_HIDDEN;
1364         }
1365       }
1366     }
1367 
1368     area->full = oldscreen;
1369     newa->full = oldscreen;
1370 
1371     ED_screen_change(C, screen);
1372   }
1373 
1374   /* XXX bad code: setscreen() ends with first area active. fullscreen render assumes this too */
1375   CTX_wm_area_set(C, screen->areabase.first);
1376 
1377   return screen->areabase.first;
1378 }
1379 
1380 /**
1381  * Wrapper to open a temporary space either as fullscreen space, or as separate window, as defined
1382  * by \a display_type.
1383  *
1384  * \param title: Title to set for the window, if a window is spawned.
1385  * \param x, y: Position of the window, if a window is spawned.
1386  * \param sizex, sizey: Dimensions of the window, if a window is spawned.
1387  */
ED_screen_temp_space_open(bContext * C,const char * title,int x,int y,int sizex,int sizey,eSpace_Type space_type,int display_type,bool dialog)1388 ScrArea *ED_screen_temp_space_open(bContext *C,
1389                                    const char *title,
1390                                    int x,
1391                                    int y,
1392                                    int sizex,
1393                                    int sizey,
1394                                    eSpace_Type space_type,
1395                                    int display_type,
1396                                    bool dialog)
1397 {
1398   ScrArea *area = NULL;
1399 
1400   switch (display_type) {
1401     case USER_TEMP_SPACE_DISPLAY_WINDOW:
1402       if (WM_window_open_temp(C, title, x, y, sizex, sizey, (int)space_type, dialog)) {
1403         area = CTX_wm_area(C);
1404       }
1405       break;
1406     case USER_TEMP_SPACE_DISPLAY_FULLSCREEN: {
1407       ScrArea *ctx_area = CTX_wm_area(C);
1408 
1409       if (ctx_area != NULL && ctx_area->full) {
1410         area = ctx_area;
1411         ED_area_newspace(C, ctx_area, space_type, true);
1412         area->flag |= AREA_FLAG_STACKED_FULLSCREEN;
1413         ((SpaceLink *)area->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY;
1414       }
1415       else if (ctx_area != NULL && ctx_area->spacetype == space_type) {
1416         area = ED_screen_state_toggle(C, CTX_wm_window(C), ctx_area, SCREENMAXIMIZED);
1417       }
1418       else {
1419         area = ED_screen_full_newspace(C, ctx_area, (int)space_type);
1420         ((SpaceLink *)area->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY;
1421       }
1422       break;
1423     }
1424   }
1425 
1426   return area;
1427 }
1428 
1429 /* update frame rate info for viewport drawing */
ED_refresh_viewport_fps(bContext * C)1430 void ED_refresh_viewport_fps(bContext *C)
1431 {
1432   wmTimer *animtimer = CTX_wm_screen(C)->animtimer;
1433   Scene *scene = CTX_data_scene(C);
1434 
1435   /* is anim playback running? */
1436   if (animtimer && (U.uiflag & USER_SHOW_FPS)) {
1437     ScreenFrameRateInfo *fpsi = scene->fps_info;
1438 
1439     /* if there isn't any info, init it first */
1440     if (fpsi == NULL) {
1441       fpsi = scene->fps_info = MEM_callocN(sizeof(ScreenFrameRateInfo),
1442                                            "refresh_viewport_fps fps_info");
1443     }
1444 
1445     /* update the values */
1446     fpsi->redrawtime = fpsi->lredrawtime;
1447     fpsi->lredrawtime = animtimer->ltime;
1448   }
1449   else {
1450     /* playback stopped or shouldn't be running */
1451     if (scene->fps_info) {
1452       MEM_freeN(scene->fps_info);
1453     }
1454     scene->fps_info = NULL;
1455   }
1456 }
1457 
1458 /* redraws: uses defines from stime->redraws
1459  * enable: 1 - forward on, -1 - backwards on, 0 - off
1460  */
ED_screen_animation_timer(bContext * C,int redraws,int sync,int enable)1461 void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable)
1462 {
1463   bScreen *screen = CTX_wm_screen(C);
1464   wmWindowManager *wm = CTX_wm_manager(C);
1465   wmWindow *win = CTX_wm_window(C);
1466   Scene *scene = CTX_data_scene(C);
1467   bScreen *stopscreen = ED_screen_animation_playing(wm);
1468 
1469   if (stopscreen) {
1470     WM_event_remove_timer(wm, win, stopscreen->animtimer);
1471     stopscreen->animtimer = NULL;
1472   }
1473 
1474   if (enable) {
1475     ScreenAnimData *sad = MEM_callocN(sizeof(ScreenAnimData), "ScreenAnimData");
1476 
1477     screen->animtimer = WM_event_add_timer(wm, win, TIMER0, (1.0 / FPS));
1478 
1479     sad->region = CTX_wm_region(C);
1480     /* If start-frame is larger than current frame, we put current-frame on start-frame.
1481      * note: first frame then is not drawn! (ton) */
1482     if (PRVRANGEON) {
1483       if (scene->r.psfra > scene->r.cfra) {
1484         sad->sfra = scene->r.cfra;
1485         scene->r.cfra = scene->r.psfra;
1486       }
1487       else {
1488         sad->sfra = scene->r.cfra;
1489       }
1490     }
1491     else {
1492       if (scene->r.sfra > scene->r.cfra) {
1493         sad->sfra = scene->r.cfra;
1494         scene->r.cfra = scene->r.sfra;
1495       }
1496       else {
1497         sad->sfra = scene->r.cfra;
1498       }
1499     }
1500     sad->redraws = redraws;
1501     sad->flag |= (enable < 0) ? ANIMPLAY_FLAG_REVERSE : 0;
1502     sad->flag |= (sync == 0) ? ANIMPLAY_FLAG_NO_SYNC : (sync == 1) ? ANIMPLAY_FLAG_SYNC : 0;
1503 
1504     ScrArea *area = CTX_wm_area(C);
1505 
1506     char spacetype = -1;
1507 
1508     if (area) {
1509       spacetype = area->spacetype;
1510     }
1511 
1512     sad->from_anim_edit = (ELEM(spacetype, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA));
1513 
1514     screen->animtimer->customdata = sad;
1515   }
1516 
1517   /* Seek audio to ensure playback in preview range with AV sync. */
1518   DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
1519 
1520   /* notifier catched by top header, for button */
1521   WM_event_add_notifier(C, NC_SCREEN | ND_ANIMPLAY, NULL);
1522 }
1523 
1524 /* helper for screen_animation_play() - only to be used for TimeLine */
time_top_left_3dwindow(bScreen * screen)1525 static ARegion *time_top_left_3dwindow(bScreen *screen)
1526 {
1527   ARegion *aret = NULL;
1528   int min = 10000;
1529 
1530   LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
1531     if (area->spacetype == SPACE_VIEW3D) {
1532       LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
1533         if (region->regiontype == RGN_TYPE_WINDOW) {
1534           if (region->winrct.xmin - region->winrct.ymin < min) {
1535             aret = region;
1536             min = region->winrct.xmin - region->winrct.ymin;
1537           }
1538         }
1539       }
1540     }
1541   }
1542 
1543   return aret;
1544 }
1545 
ED_screen_animation_timer_update(bScreen * screen,int redraws)1546 void ED_screen_animation_timer_update(bScreen *screen, int redraws)
1547 {
1548   if (screen && screen->animtimer) {
1549     wmTimer *wt = screen->animtimer;
1550     ScreenAnimData *sad = wt->customdata;
1551 
1552     sad->redraws = redraws;
1553     sad->region = NULL;
1554     if (redraws & TIME_REGION) {
1555       sad->region = time_top_left_3dwindow(screen);
1556     }
1557   }
1558 }
1559 
1560 /* results in fully updated anim system */
ED_update_for_newframe(Main * bmain,Depsgraph * depsgraph)1561 void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph)
1562 {
1563   Scene *scene = DEG_get_input_scene(depsgraph);
1564 
1565   DEG_time_tag_update(bmain);
1566 
1567 #ifdef DURIAN_CAMERA_SWITCH
1568   void *camera = BKE_scene_camera_switch_find(scene);
1569   if (camera && scene->camera != camera) {
1570     scene->camera = camera;
1571     /* are there cameras in the views that are not in the scene? */
1572     LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
1573       BKE_screen_view3d_scene_sync(screen, scene);
1574     }
1575     DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
1576   }
1577 #endif
1578 
1579   ED_clip_update_frame(bmain, scene->r.cfra);
1580 
1581   /* this function applies the changes too */
1582   BKE_scene_graph_update_for_newframe(depsgraph);
1583 }
1584 
1585 /*
1586  * return true if any active area requires to see in 3D
1587  */
ED_screen_stereo3d_required(const bScreen * screen,const Scene * scene)1588 bool ED_screen_stereo3d_required(const bScreen *screen, const Scene *scene)
1589 {
1590   const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0;
1591 
1592   LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
1593     switch (area->spacetype) {
1594       case SPACE_VIEW3D: {
1595         View3D *v3d;
1596 
1597         if (!is_multiview) {
1598           continue;
1599         }
1600 
1601         v3d = area->spacedata.first;
1602         if (v3d->camera && v3d->stereo3d_camera == STEREO_3D_ID) {
1603           LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
1604             if (region->regiondata && region->regiontype == RGN_TYPE_WINDOW) {
1605               RegionView3D *rv3d = region->regiondata;
1606               if (rv3d->persp == RV3D_CAMOB) {
1607                 return true;
1608               }
1609             }
1610           }
1611         }
1612         break;
1613       }
1614       case SPACE_IMAGE: {
1615         SpaceImage *sima;
1616 
1617         /* images should always show in stereo, even if
1618          * the file doesn't have views enabled */
1619         sima = area->spacedata.first;
1620         if (sima->image && BKE_image_is_stereo(sima->image) &&
1621             (sima->iuser.flag & IMA_SHOW_STEREO)) {
1622           return true;
1623         }
1624         break;
1625       }
1626       case SPACE_NODE: {
1627         SpaceNode *snode;
1628 
1629         if (!is_multiview) {
1630           continue;
1631         }
1632 
1633         snode = area->spacedata.first;
1634         if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) {
1635           return true;
1636         }
1637         break;
1638       }
1639       case SPACE_SEQ: {
1640         SpaceSeq *sseq;
1641 
1642         if (!is_multiview) {
1643           continue;
1644         }
1645 
1646         sseq = area->spacedata.first;
1647         if (ELEM(sseq->view, SEQ_VIEW_PREVIEW, SEQ_VIEW_SEQUENCE_PREVIEW)) {
1648           return true;
1649         }
1650 
1651         if (sseq->draw_flag & SEQ_DRAW_BACKDROP) {
1652           return true;
1653         }
1654 
1655         break;
1656       }
1657     }
1658   }
1659 
1660   return false;
1661 }
1662 
1663 /**
1664  * Find the scene displayed in \a screen.
1665  * \note Assumes \a screen to be visible/active!
1666  */
1667 
ED_screen_scene_find_with_window(const bScreen * screen,const wmWindowManager * wm,struct wmWindow ** r_window)1668 Scene *ED_screen_scene_find_with_window(const bScreen *screen,
1669                                         const wmWindowManager *wm,
1670                                         struct wmWindow **r_window)
1671 {
1672   LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
1673     if (WM_window_get_active_screen(win) == screen) {
1674       if (r_window) {
1675         *r_window = win;
1676       }
1677       return WM_window_get_active_scene(win);
1678     }
1679   }
1680 
1681   /* Can by NULL when accessing a screen that isn't active. */
1682   return NULL;
1683 }
1684 
ED_screen_area_find_with_spacedata(const bScreen * screen,const SpaceLink * sl,const bool only_visible)1685 ScrArea *ED_screen_area_find_with_spacedata(const bScreen *screen,
1686                                             const SpaceLink *sl,
1687                                             const bool only_visible)
1688 {
1689   if (only_visible) {
1690     LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
1691       if (area->spacedata.first == sl) {
1692         return area;
1693       }
1694     }
1695   }
1696   else {
1697     LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
1698       if (BLI_findindex(&area->spacedata, sl) != -1) {
1699         return area;
1700       }
1701     }
1702   }
1703   return NULL;
1704 }
1705 
ED_screen_scene_find(const bScreen * screen,const wmWindowManager * wm)1706 Scene *ED_screen_scene_find(const bScreen *screen, const wmWindowManager *wm)
1707 {
1708   return ED_screen_scene_find_with_window(screen, wm, NULL);
1709 }
1710 
ED_screen_window_find(const bScreen * screen,const wmWindowManager * wm)1711 wmWindow *ED_screen_window_find(const bScreen *screen, const wmWindowManager *wm)
1712 {
1713   LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
1714     if (WM_window_get_active_screen(win) == screen) {
1715       return win;
1716     }
1717   }
1718   return NULL;
1719 }
1720