Merging r59153 through r59161 from trunk into soc-2013-depsgraph_mt
[blender.git] / source / blender / windowmanager / intern / wm_playanim.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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Campbell Barton
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/windowmanager/intern/wm_playanim.c
29  *  \ingroup wm
30  *
31  * \note This file uses ghost directly and none of the WM definitions.
32  *       this could be made into its own module, alongside creator/
33  */
34
35 #include <sys/types.h>
36 #include <fcntl.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <math.h>
40
41 #ifndef WIN32
42 #  include <unistd.h>
43 #  include <sys/times.h>
44 #  include <sys/wait.h>
45 #else
46 #  include <io.h>
47 #endif
48 #include "MEM_guardedalloc.h"
49
50 #include "PIL_time.h"
51
52 #include "BLI_utildefines.h"
53 #include "BLI_fileops.h"
54 #include "BLI_listbase.h"
55 #include "BLI_path_util.h"
56 #include "BLI_rect.h"
57 #include "BLI_string.h"
58
59 #include "IMB_imbuf_types.h"
60 #include "IMB_imbuf.h"
61
62 #include "BKE_blender.h"
63 #include "BKE_depsgraph.h"
64 #include "BKE_global.h"
65 #include "BKE_image.h"
66
67 #include "BIF_gl.h"
68 #include "BIF_glutil.h"
69
70 #include "DNA_scene_types.h"
71 #include "ED_datafiles.h" /* for fonts */
72 #include "GHOST_C-api.h"
73 #include "BLF_api.h"
74
75 #include "wm_event_types.h"
76
77 #include "WM_api.h"  /* only for WM_main_playanim */
78
79 struct PlayState;
80 static void playanim_window_zoom(struct PlayState *ps, const float zoom_offset);
81
82 typedef struct PlayState {
83
84         /* window and viewport size */
85         int win_x, win_y;
86         
87         /* current zoom level */
88         float zoom;
89
90         /* playback state */
91         short direction;
92         short next_frame;
93         short once;
94         short turbo;
95         short pingpong;
96         short noskip;
97         short sstep;
98         short wait2;
99         short stopped;
100         short go;
101         
102         int fstep;
103
104         /* current picture */
105         struct PlayAnimPict *picture;
106
107         /* set once at the start */
108         int ibufx, ibufy;
109         int fontid;
110
111         /* saves passing args */
112         struct ImBuf *curframe_ibuf;
113         
114         /* restarts player for file drop */
115         char dropped_file[FILE_MAX];
116 } PlayState;
117
118 /* for debugging */
119 #if 0
120 void print_ps(PlayState *ps)
121 {
122         printf("ps:\n");
123         printf("    direction=%d,\n", (int)ps->direction);
124         printf("    next=%d,\n", ps->next);
125         printf("    once=%d,\n", ps->once);
126         printf("    turbo=%d,\n", ps->turbo);
127         printf("    pingpong=%d,\n", ps->pingpong);
128         printf("    noskip=%d,\n", ps->noskip);
129         printf("    sstep=%d,\n", ps->sstep);
130         printf("    pause=%d,\n", ps->pause);
131         printf("    wait2=%d,\n", ps->wait2);
132         printf("    stopped=%d,\n", ps->stopped);
133         printf("    go=%d,\n\n", ps->go);
134         fflush(stdout);
135 }
136 #endif
137
138 /* global for window and events */
139 typedef enum eWS_Qual {
140         WS_QUAL_LSHIFT  = (1 << 0),
141         WS_QUAL_RSHIFT  = (1 << 1),
142         WS_QUAL_SHIFT   = (WS_QUAL_LSHIFT | WS_QUAL_RSHIFT),
143         WS_QUAL_LALT    = (1 << 2),
144         WS_QUAL_RALT    = (1 << 3),
145         WS_QUAL_ALT     = (WS_QUAL_LALT | WS_QUAL_RALT),
146         WS_QUAL_LCTRL   = (1 << 4),
147         WS_QUAL_RCTRL   = (1 << 5),
148         WS_QUAL_CTRL    = (WS_QUAL_LCTRL | WS_QUAL_RCTRL),
149         WS_QUAL_LMOUSE  = (1 << 16),
150         WS_QUAL_MMOUSE  = (1 << 17),
151         WS_QUAL_RMOUSE  = (1 << 18),
152         WS_QUAL_MOUSE   = (WS_QUAL_LMOUSE | WS_QUAL_MMOUSE | WS_QUAL_RMOUSE)
153 } eWS_Qual;
154
155 static struct WindowStateGlobal {
156         GHOST_SystemHandle ghost_system;
157         void *ghost_window;
158
159         /* events */
160         eWS_Qual qual;
161 } g_WS = {NULL};
162
163 static void playanim_window_get_size(int *width_r, int *height_r)
164 {
165         GHOST_RectangleHandle bounds = GHOST_GetClientBounds(g_WS.ghost_window);
166         *width_r = GHOST_GetWidthRectangle(bounds);
167         *height_r = GHOST_GetHeightRectangle(bounds);
168         GHOST_DisposeRectangle(bounds);
169 }
170
171 /* implementation */
172 static void playanim_event_qual_update(void)
173 {
174         int val;
175
176         /* Shift */
177         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftShift, &val);
178         if (val) g_WS.qual |=  WS_QUAL_LSHIFT;
179         else     g_WS.qual &= ~WS_QUAL_LSHIFT;
180
181         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyRightShift, &val);
182         if (val) g_WS.qual |=  WS_QUAL_RSHIFT;
183         else     g_WS.qual &= ~WS_QUAL_RSHIFT;
184
185         /* Control */
186         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftControl, &val);
187         if (val) g_WS.qual |=  WS_QUAL_LCTRL;
188         else     g_WS.qual &= ~WS_QUAL_LCTRL;
189
190         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyRightControl, &val);
191         if (val) g_WS.qual |=  WS_QUAL_RCTRL;
192         else     g_WS.qual &= ~WS_QUAL_RCTRL;
193
194         /* Alt */
195         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftAlt, &val);
196         if (val) g_WS.qual |=  WS_QUAL_LALT;
197         else     g_WS.qual &= ~WS_QUAL_LALT;
198
199         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyRightAlt, &val);
200         if (val) g_WS.qual |=  WS_QUAL_RALT;
201         else     g_WS.qual &= ~WS_QUAL_RALT;
202 }
203
204 typedef struct PlayAnimPict {
205         struct PlayAnimPict *next, *prev;
206         char *mem;
207         int size;
208         char *name;
209         struct ImBuf *ibuf;
210         struct anim *anim;
211         int frame;
212         int IB_flags;
213 } PlayAnimPict;
214
215 static struct ListBase picsbase = {NULL, NULL};
216 static int fromdisk = FALSE;
217 static double ptottime = 0.0, swaptime = 0.04;
218
219 static PlayAnimPict *playanim_step(PlayAnimPict *playanim, int step)
220 {
221         if (step > 0) {
222                 while (step-- && playanim) {
223                         playanim = playanim->next;
224                 }
225         }
226         else if (step < 0) {
227                 while (step++ && playanim) {
228                         playanim = playanim->prev;
229                 }
230         }
231         return playanim;
232 }
233
234 static int pupdate_time(void)
235 {
236         static double ltime;
237         double time;
238
239         time = PIL_check_seconds_timer();
240
241         ptottime += (time - ltime);
242         ltime = time;
243         return (ptottime < 0);
244 }
245
246 static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf *ibuf, int fontid, int fstep)
247 {
248         float offsx, offsy;
249
250         if (ibuf == NULL) {
251                 printf("%s: no ibuf for picture '%s'\n", __func__, picture ? picture->name : "<NIL>");
252                 return;
253         }
254         if (ibuf->rect == NULL && ibuf->rect_float) {
255                 IMB_rect_from_float(ibuf);
256                 imb_freerectfloatImBuf(ibuf);
257         }
258         if (ibuf->rect == NULL)
259                 return;
260
261         GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
262
263         /* offset within window */
264         offsx = 0.5f * (((float)ps->win_x - ps->zoom * ibuf->x) / (float)ps->win_x);
265         offsy = 0.5f * (((float)ps->win_y - ps->zoom * ibuf->y) / (float)ps->win_y);
266
267         CLAMP(offsx, 0.0f, 1.0f);
268         CLAMP(offsy, 0.0f, 1.0f);
269         glRasterPos2f(offsx, offsy);
270
271         glClearColor(0.1, 0.1, 0.1, 0.0);
272         glClear(GL_COLOR_BUFFER_BIT);
273         
274         /* checkerboard for case alpha */
275         if (ibuf->planes == 32) {
276                 glEnable(GL_BLEND);
277                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
278
279                 fdrawcheckerboard(offsx, offsy, offsx + (ps->zoom * ibuf->x) / (float)ps->win_x, offsy + (ps->zoom * ibuf->y) / (float)ps->win_y);
280         }
281         
282         glDrawPixels(ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
283
284         glDisable(GL_BLEND);
285         
286         pupdate_time();
287
288         if (picture && (g_WS.qual & (WS_QUAL_SHIFT | WS_QUAL_LMOUSE)) && (fontid != -1)) {
289                 int sizex, sizey;
290                 float fsizex_inv, fsizey_inv;
291                 char str[32 + FILE_MAX];
292                 cpack(-1);
293                 BLI_snprintf(str, sizeof(str), "%s | %.2f frames/s", picture->name, fstep / swaptime);
294
295                 playanim_window_get_size(&sizex, &sizey);
296                 fsizex_inv = 1.0f / sizex;
297                 fsizey_inv = 1.0f / sizey;
298
299                 BLF_enable(fontid, BLF_ASPECT);
300                 BLF_aspect(fontid, fsizex_inv, fsizey_inv, 1.0f);
301                 BLF_position(fontid, 10.0f * fsizex_inv, 10.0f * fsizey_inv, 0.0f);
302                 BLF_draw(fontid, str, sizeof(str));
303         }
304
305         GHOST_SwapWindowBuffers(g_WS.ghost_window);
306 }
307
308 static void build_pict_list(PlayState *ps, char *first, int totframes, int fstep, int fontid)
309 {
310         char *mem, filepath[FILE_MAX];
311 //      short val;
312         PlayAnimPict *picture = NULL;
313         struct ImBuf *ibuf = NULL;
314         char str[32 + FILE_MAX];
315         struct anim *anim;
316
317         if (IMB_isanim(first)) {
318                 /* OCIO_TODO: support different input color space */
319                 anim = IMB_open_anim(first, IB_rect, 0, NULL);
320                 if (anim) {
321                         int pic;
322                         ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
323                         if (ibuf) {
324                                 playanim_toscreen(ps, NULL, ibuf, fontid, fstep);
325                                 IMB_freeImBuf(ibuf);
326                         }
327
328                         for (pic = 0; pic < IMB_anim_get_duration(anim, IMB_TC_NONE); pic++) {
329                                 picture = (PlayAnimPict *)MEM_callocN(sizeof(PlayAnimPict), "Pict");
330                                 picture->anim = anim;
331                                 picture->frame = pic;
332                                 picture->IB_flags = IB_rect;
333                                 BLI_snprintf(str, sizeof(str), "%s : %4.d", first, pic + 1);
334                                 picture->name = strdup(str);
335                                 BLI_addtail(&picsbase, picture);
336                         }
337                 }
338                 else {
339                         printf("couldn't open anim %s\n", first);
340                 }
341         }
342         else {
343                 int count = 0;
344
345                 BLI_strncpy(filepath, first, sizeof(filepath));
346
347                 pupdate_time();
348                 ptottime = 1.0;
349
350                 /* O_DIRECT
351                  *
352                  * If set, all reads and writes on the resulting file descriptor will
353                  * be performed directly to or from the user program buffer, provided
354                  * appropriate size and alignment restrictions are met.  Refer to the
355                  * F_SETFL and F_DIOINFO commands in the fcntl(2) manual entry for
356                  * information about how to determine the alignment constraints.
357                  * O_DIRECT is a Silicon Graphics extension and is only supported on
358                  * local EFS and XFS file systems.
359                  */
360
361                 while (IMB_ispic(filepath) && totframes) {
362                         size_t size;
363                         int file;
364
365                         file = open(filepath, O_BINARY | O_RDONLY, 0);
366                         if (file < 0) {
367                                 /* print errno? */
368                                 return;
369                         }
370
371                         picture = (PlayAnimPict *)MEM_callocN(sizeof(PlayAnimPict), "picture");
372                         if (picture == NULL) {
373                                 printf("Not enough memory for pict struct '%s'\n", filepath);
374                                 close(file);
375                                 return;
376                         }
377                         size = BLI_file_descriptor_size(file);
378
379                         if (size < 1) {
380                                 close(file);
381                                 MEM_freeN(picture);
382                                 return;
383                         }
384
385                         picture->size = size;
386                         picture->IB_flags = IB_rect;
387
388                         if (fromdisk == FALSE) {
389                                 mem = (char *)MEM_mallocN(size, "build pic list");
390                                 if (mem == NULL) {
391                                         printf("Couldn't get memory\n");
392                                         close(file);
393                                         MEM_freeN(picture);
394                                         return;
395                                 }
396
397                                 if (read(file, mem, size) != size) {
398                                         printf("Error while reading %s\n", filepath);
399                                         close(file);
400                                         MEM_freeN(picture);
401                                         MEM_freeN(mem);
402                                         return;
403                                 }
404                         }
405                         else {
406                                 mem = NULL;
407                         }
408
409                         picture->mem = mem;
410                         picture->name = strdup(filepath);
411                         close(file);
412                         BLI_addtail(&picsbase, picture);
413                         count++;
414
415                         pupdate_time();
416
417                         if (ptottime > 1.0) {
418                                 /* OCIO_TODO: support different input color space */
419                                 if (picture->mem) {
420                                         ibuf = IMB_ibImageFromMemory((unsigned char *)picture->mem, picture->size,
421                                                                      picture->IB_flags, NULL, picture->name);
422                                 }
423                                 else {
424                                         ibuf = IMB_loadiffname(picture->name, picture->IB_flags, NULL);
425                                 }
426                                 if (ibuf) {
427                                         playanim_toscreen(ps, picture, ibuf, fontid, fstep);
428                                         IMB_freeImBuf(ibuf);
429                                 }
430                                 pupdate_time();
431                                 ptottime = 0.0;
432                         }
433
434                         BLI_newname(filepath, +fstep);
435
436 #if 0 // XXX25
437                         while (qtest()) {
438                                 switch (qreadN(&val)) {
439                                         case ESCKEY:
440                                                 if (val) return;
441                                                 break;
442                                 }
443                         }
444 #endif
445                         totframes--;
446                 }
447         }
448         return;
449 }
450
451 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
452 {
453         PlayState *ps = (PlayState *)ps_void;
454         GHOST_TEventType type = GHOST_GetEventType(evt);
455         int val;
456
457         // print_ps(ps);
458
459         playanim_event_qual_update();
460
461         /* convert ghost event into value keyboard or mouse */
462         val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
463
464         if (ps->wait2 && ps->stopped) {
465                 ps->stopped = FALSE;
466         }
467
468         if (ps->wait2) {
469                 pupdate_time();
470                 ptottime = 0;
471         }
472
473         switch (type) {
474                 case GHOST_kEventKeyDown:
475                 case GHOST_kEventKeyUp:
476                 {
477                         GHOST_TEventKeyData *key_data;
478
479                         key_data = (GHOST_TEventKeyData *)GHOST_GetEventData(evt);
480                         switch (key_data->key) {
481                                 case GHOST_kKeyA:
482                                         if (val) ps->noskip = !ps->noskip;
483                                         break;
484                                 case GHOST_kKeyP:
485                                         if (val) ps->pingpong = !ps->pingpong;
486                                         break;
487                                 case GHOST_kKey1:
488                                 case GHOST_kKeyNumpad1:
489                                         if (val) swaptime = ps->fstep / 60.0;
490                                         break;
491                                 case GHOST_kKey2:
492                                 case GHOST_kKeyNumpad2:
493                                         if (val) swaptime = ps->fstep / 50.0;
494                                         break;
495                                 case GHOST_kKey3:
496                                 case GHOST_kKeyNumpad3:
497                                         if (val) swaptime = ps->fstep / 30.0;
498                                         break;
499                                 case GHOST_kKey4:
500                                 case GHOST_kKeyNumpad4:
501                                         if (g_WS.qual & WS_QUAL_SHIFT)
502                                                 swaptime = ps->fstep / 24.0;
503                                         else
504                                                 swaptime = ps->fstep / 25.0;
505                                         break;
506                                 case GHOST_kKey5:
507                                 case GHOST_kKeyNumpad5:
508                                         if (val) swaptime = ps->fstep / 20.0;
509                                         break;
510                                 case GHOST_kKey6:
511                                 case GHOST_kKeyNumpad6:
512                                         if (val) swaptime = ps->fstep / 15.0;
513                                         break;
514                                 case GHOST_kKey7:
515                                 case GHOST_kKeyNumpad7:
516                                         if (val) swaptime = ps->fstep / 12.0;
517                                         break;
518                                 case GHOST_kKey8:
519                                 case GHOST_kKeyNumpad8:
520                                         if (val) swaptime = ps->fstep / 10.0;
521                                         break;
522                                 case GHOST_kKey9:
523                                 case GHOST_kKeyNumpad9:
524                                         if (val) swaptime = ps->fstep / 6.0;
525                                         break;
526                                 case GHOST_kKeyLeftArrow:
527                                         if (val) {
528                                                 ps->sstep = TRUE;
529                                                 ps->wait2 = FALSE;
530                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
531                                                         ps->picture = picsbase.first;
532                                                         ps->next_frame = 0;
533                                                 }
534                                                 else {
535                                                         ps->next_frame = -1;
536                                                 }
537                                         }
538                                         break;
539                                 case GHOST_kKeyDownArrow:
540                                         if (val) {
541                                                 ps->wait2 = FALSE;
542                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
543                                                         ps->next_frame = ps->direction = -1;
544                                                 }
545                                                 else {
546                                                         ps->next_frame = -10;
547                                                         ps->sstep = TRUE;
548                                                 }
549                                         }
550                                         break;
551                                 case GHOST_kKeyRightArrow:
552                                         if (val) {
553                                                 ps->sstep = TRUE;
554                                                 ps->wait2 = FALSE;
555                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
556                                                         ps->picture = picsbase.last;
557                                                         ps->next_frame = 0;
558                                                 }
559                                                 else {
560                                                         ps->next_frame = 1;
561                                                 }
562                                         }
563                                         break;
564                                 case GHOST_kKeyUpArrow:
565                                         if (val) {
566                                                 ps->wait2 = FALSE;
567                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
568                                                         ps->next_frame = ps->direction = 1;
569                                                 }
570                                                 else {
571                                                         ps->next_frame = 10;
572                                                         ps->sstep = TRUE;
573                                                 }
574                                         }
575                                         break;
576
577                                 case GHOST_kKeySlash:
578                                 case GHOST_kKeyNumpadSlash:
579                                         if (val) {
580                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
581                                                         if (ps->curframe_ibuf)
582                                                                 printf(" Name: %s | Speed: %.2f frames/s\n",
583                                                                        ps->curframe_ibuf->name, ps->fstep / swaptime);
584                                                 }
585                                                 else {
586                                                         swaptime = ps->fstep / 5.0;
587                                                 }
588                                         }
589                                         break;
590                                 case GHOST_kKey0:
591                                 case GHOST_kKeyNumpad0:
592                                         if (val) {
593                                                 if (ps->once) {
594                                                         ps->once = ps->wait2 = FALSE;
595                                                 }
596                                                 else {
597                                                         ps->picture = NULL;
598                                                         ps->once = TRUE;
599                                                         ps->wait2 = FALSE;
600                                                 }
601                                         }
602                                         break;
603                                 case GHOST_kKeyEnter:
604                                 case GHOST_kKeyNumpadEnter:
605                                         if (val) {
606                                                 ps->wait2 = ps->sstep = FALSE;
607                                         }
608                                         break;
609                                 case GHOST_kKeyPeriod:
610                                 case GHOST_kKeyNumpadPeriod:
611                                         if (val) {
612                                                 if (ps->sstep) {
613                                                         ps->wait2 = FALSE;
614                                                 }
615                                                 else {
616                                                         ps->sstep = TRUE;
617                                                         ps->wait2 = !ps->wait2;
618                                                 }
619                                         }
620                                         break;
621                                 case GHOST_kKeyEqual:
622                                 case GHOST_kKeyNumpadPlus:
623                                 {
624                                         if (val == 0) break;
625                                         if (g_WS.qual & WS_QUAL_CTRL) {
626                                                 playanim_window_zoom(ps, 1.0f);
627                                         }
628                                         else {
629                                                 swaptime /= 1.1;
630                                         }
631                                         break;
632                                 }
633                                 case GHOST_kKeyMinus:
634                                 case GHOST_kKeyNumpadMinus:
635                                 {
636                                         if (val == 0) break;
637                                         if (g_WS.qual & WS_QUAL_CTRL) {
638                                                 playanim_window_zoom(ps, -1.0f);
639                                         }
640                                         else {
641                                                 swaptime *= 1.1;
642                                         }
643                                         break;
644                                 }
645                                 case GHOST_kKeyEsc:
646                                         ps->go = FALSE;
647                                         break;
648                                 default:
649                                         break;
650                         }
651                         break;
652                 }
653                 case GHOST_kEventButtonDown:
654                 case GHOST_kEventButtonUp:
655                 {
656                         GHOST_TEventButtonData *bd = GHOST_GetEventData(evt);
657                         int cx, cy, sizex, sizey, inside_window;
658                         
659                         GHOST_GetCursorPosition(g_WS.ghost_system, &cx, &cy);
660                         GHOST_ScreenToClient(g_WS.ghost_window, cx, cy, &cx, &cy);
661                         playanim_window_get_size(&sizex, &sizey);
662
663                         inside_window = (cx >= 0 && cx < sizex && cy >= 0 && cy <= sizey);
664                         
665                         if (bd->button == GHOST_kButtonMaskLeft) {
666                                 if (type == GHOST_kEventButtonDown) {
667                                         if (inside_window)
668                                                 g_WS.qual |= WS_QUAL_LMOUSE;
669                                 }
670                                 else
671                                         g_WS.qual &= ~WS_QUAL_LMOUSE;
672                         }
673                         else if (bd->button == GHOST_kButtonMaskMiddle) {
674                                 if (type == GHOST_kEventButtonDown) {
675                                         if (inside_window)
676                                                 g_WS.qual |= WS_QUAL_MMOUSE;
677                                 }
678                                 else
679                                         g_WS.qual &= ~WS_QUAL_MMOUSE;
680                         }
681                         else if (bd->button == GHOST_kButtonMaskRight) {
682                                 if (type == GHOST_kEventButtonDown) {
683                                         if (inside_window)
684                                                 g_WS.qual |= WS_QUAL_RMOUSE;
685                                 }
686                                 else
687                                         g_WS.qual &= ~WS_QUAL_RMOUSE;
688                         }
689                         break;
690                 }
691                 case GHOST_kEventCursorMove:
692                 {
693                         if (g_WS.qual & WS_QUAL_LMOUSE) {
694                                 int sizex, sizey;
695                                 int i;
696
697                                 GHOST_TEventCursorData *cd = GHOST_GetEventData(evt);
698                                 int cx, cy;
699
700                                 GHOST_ScreenToClient(g_WS.ghost_window, cd->x, cd->y, &cx, &cy);
701
702                                 playanim_window_get_size(&sizex, &sizey);
703                                 ps->picture = picsbase.first;
704                                 /* TODO - store in ps direct? */
705                                 i = 0;
706                                 while (ps->picture) {
707                                         i++;
708                                         ps->picture = ps->picture->next;
709                                 }
710                                 i = (i * cx) / sizex;
711                                 ps->picture = picsbase.first;
712                                 for (; i > 0; i--) {
713                                         if (ps->picture->next == NULL) break;
714                                         ps->picture = ps->picture->next;
715                                 }
716                                 ps->sstep = TRUE;
717                                 ps->wait2 = FALSE;
718                                 ps->next_frame = 0;
719                         }
720                         break;
721                 }
722                 case GHOST_kEventWindowActivate:
723                 case GHOST_kEventWindowDeactivate:
724                 {
725                         g_WS.qual &= ~WS_QUAL_MOUSE;
726                         break;
727                 }
728                 case GHOST_kEventWindowSize:
729                 case GHOST_kEventWindowMove:
730                 {
731                         float zoomx, zoomy;
732                         
733                         playanim_window_get_size(&ps->win_x, &ps->win_y);
734                         GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
735
736                         zoomx = (float) ps->win_x / ps->ibufx;
737                         zoomy = (float) ps->win_y / ps->ibufy;
738                         
739                         /* zoom always show entire image */
740                         ps->zoom = MIN2(zoomx, zoomy);
741                         
742                         /* zoom steps of 2 for speed */
743                         ps->zoom = floor(ps->zoom + 0.5f);
744                         if (ps->zoom < 1.0f) ps->zoom = 1.0f;
745                         
746                         glViewport(0, 0, ps->win_x, ps->win_y);
747                         glScissor(0, 0, ps->win_x, ps->win_y);
748                         
749                         /* unified matrix, note it affects offset for drawing */
750                         glMatrixMode(GL_PROJECTION);
751                         glLoadIdentity();
752                         glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
753                         glMatrixMode(GL_MODELVIEW);
754
755                         glPixelZoom(ps->zoom, ps->zoom);
756                         ptottime = 0.0;
757                         playanim_toscreen(ps, ps->picture, ps->curframe_ibuf, ps->fontid, ps->fstep);
758
759                         break;
760                 }
761                 case GHOST_kEventQuit:
762                 case GHOST_kEventWindowClose:
763                 {
764                         ps->go = FALSE;
765                         break;
766                 }
767                 case GHOST_kEventDraggingDropDone:
768                 {
769                         GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt);
770                         
771                         if (ddd->dataType == GHOST_kDragnDropTypeFilenames) {
772                                 GHOST_TStringArray *stra = ddd->data;
773                                 int a;
774                                 
775                                 for (a = 0; a < stra->count; a++) {
776                                         BLI_strncpy(ps->dropped_file, (char *)stra->strings[a], sizeof(ps->dropped_file));
777                                         ps->go = FALSE;
778                                         printf("drop file %s\n", stra->strings[a]);
779                                         break; /* only one drop element supported now */
780                                 }
781                         }
782                         break;
783                 }
784                 default:
785                         /* quiet warnings */
786                         break;
787         }
788
789         return 1;
790 }
791
792 static void playanim_window_open(const char *title, int posx, int posy, int sizex, int sizey)
793 {
794         GHOST_TUns32 scr_w, scr_h;
795
796         GHOST_GetMainDisplayDimensions(g_WS.ghost_system, &scr_w, &scr_h);
797
798         posy = (scr_h - posy - sizey);
799
800         g_WS.ghost_window = GHOST_CreateWindow(g_WS.ghost_system,
801                                                title,
802                                                posx, posy, sizex, sizey,
803                                                /* could optionally start fullscreen */
804                                                GHOST_kWindowStateNormal,
805                                                GHOST_kDrawingContextTypeOpenGL,
806                                                FALSE /* no stereo */, FALSE);
807 }
808
809 static void playanim_window_zoom(PlayState *ps, const float zoom_offset)
810 {
811         int sizex, sizey;
812         /* int ofsx, ofsy; */ /* UNUSED */
813
814         if (ps->zoom + zoom_offset > 0.0f) ps->zoom += zoom_offset;
815
816         // playanim_window_get_position(&ofsx, &ofsy);
817         playanim_window_get_size(&sizex, &sizey);
818         /* ofsx += sizex / 2; */ /* UNUSED */
819         /* ofsy += sizey / 2; */ /* UNUSED */
820         sizex = ps->zoom * ps->ibufx;
821         sizey = ps->zoom * ps->ibufy;
822         /* ofsx -= sizex / 2; */ /* UNUSED */
823         /* ofsy -= sizey / 2; */ /* UNUSED */
824         // window_set_position(g_WS.ghost_window, sizex, sizey);
825         GHOST_SetClientSize(g_WS.ghost_window, sizex, sizey);
826 }
827
828 /* return path for restart */
829 static char *wm_main_playanim_intern(int argc, const char **argv)
830 {
831         struct ImBuf *ibuf = NULL;
832         static char filepath[FILE_MAX]; /* abused to return dropped file path */
833         GHOST_TUns32 maxwinx, maxwiny;
834         int i;
835         /* This was done to disambiguate the name for use under c++. */
836         struct anim *anim = NULL;
837         int start_x = 0, start_y = 0;
838         int sfra = -1;
839         int efra = -1;
840         int totblock;
841         
842         PlayState ps = {0};
843
844         /* ps.doubleb   = TRUE;*/ /* UNUSED */
845         ps.go        = TRUE;
846         ps.direction = TRUE;
847         ps.next_frame = 1;
848         ps.once      = FALSE;
849         ps.turbo     = FALSE;
850         ps.pingpong  = FALSE;
851         ps.noskip    = FALSE;
852         ps.sstep     = FALSE;
853         ps.wait2     = FALSE;
854         ps.stopped   = FALSE;
855         ps.picture   = NULL;
856         ps.dropped_file[0] = 0;
857         ps.zoom      = 1.0f;
858         /* resetmap = FALSE */
859
860         ps.fstep     = 1;
861
862         ps.fontid = -1;
863
864         while (argc > 1) {
865                 if (argv[1][0] == '-') {
866                         switch (argv[1][1]) {
867                                 case 'm':
868                                         fromdisk = TRUE;
869                                         break;
870                                 case 'p':
871                                         if (argc > 3) {
872                                                 start_x = atoi(argv[2]);
873                                                 start_y = atoi(argv[3]);
874                                                 argc -= 2;
875                                                 argv += 2;
876                                         }
877                                         else {
878                                                 printf("too few arguments for -p (need 2): skipping\n");
879                                         }
880                                         break;
881                                 case 'f':
882                                         if (argc > 3) {
883                                                 double fps = atof(argv[2]);
884                                                 double fps_base = atof(argv[3]);
885                                                 if (fps == 0.0) {
886                                                         fps = 1;
887                                                         printf("invalid fps,"
888                                                                "forcing 1\n");
889                                                 }
890                                                 swaptime = fps_base / fps;
891                                                 argc -= 2;
892                                                 argv += 2;
893                                         }
894                                         else {
895                                                 printf("too few arguments for -f (need 2): skipping\n");
896                                         }
897                                         break;
898                                 case 's':
899                                         sfra = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]) ));
900                                         argc--;
901                                         argv++;
902                                         break;
903                                 case 'e':
904                                         efra = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]) ));
905                                         argc--;
906                                         argv++;
907                                         break;
908                                 case 'j':
909                                         ps.fstep = MIN2(MAXFRAME, MAX2(1, atoi(argv[2])));
910                                         swaptime *= ps.fstep;
911                                         argc--;
912                                         argv++;
913                                         break;
914                                 default:
915                                         printf("unknown option '%c': skipping\n", argv[1][1]);
916                                         break;
917                         }
918                         argc--;
919                         argv++;
920                 }
921                 else {
922                         break;
923                 }
924         }
925
926         if (argc > 1) {
927                 BLI_strncpy(filepath, argv[1], sizeof(filepath));
928         }
929         else {
930                 BLI_current_working_dir(filepath, sizeof(filepath));
931                 BLI_add_slash(filepath);
932         }
933
934         if (IMB_isanim(filepath)) {
935                 /* OCIO_TODO: support different input color spaces */
936                 anim = IMB_open_anim(filepath, IB_rect, 0, NULL);
937                 if (anim) {
938                         ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
939                         IMB_close_anim(anim);
940                         anim = NULL;
941                 }
942         }
943         else if (!IMB_ispic(filepath)) {
944                 printf("%s: '%s' not an image file\n", __func__, filepath);
945                 exit(1);
946         }
947
948         if (ibuf == NULL) {
949                 /* OCIO_TODO: support different input color space */
950                 ibuf = IMB_loadiffname(filepath, IB_rect, NULL);
951         }
952
953         if (ibuf == NULL) {
954                 printf("%s: '%s' couldn't open\n", __func__, filepath);
955                 exit(1);
956         }
957
958 #if 0 //XXX25
959         #if !defined(WIN32) && !defined(__APPLE__)
960         if (fork()) exit(0);
961         #endif
962 #endif //XXX25
963
964         {
965
966                 GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, &ps);
967
968                 g_WS.ghost_system = GHOST_CreateSystem();
969                 GHOST_AddEventConsumer(g_WS.ghost_system, consumer);
970
971                 playanim_window_open("Blender:Anim", start_x, start_y, ibuf->x, ibuf->y);
972
973                 /* unified matrix, note it affects offset for drawing */
974                 glMatrixMode(GL_PROJECTION);
975                 glLoadIdentity();
976                 glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
977                 glMatrixMode(GL_MODELVIEW);
978         }
979
980         GHOST_GetMainDisplayDimensions(g_WS.ghost_system, &maxwinx, &maxwiny);
981
982         //GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
983
984         /* initialize the font */
985         BLF_init(11, 72);
986         ps.fontid = BLF_load_mem("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
987         BLF_size(ps.fontid, 11, 72);
988
989         ps.ibufx = ibuf->x;
990         ps.ibufy = ibuf->y;
991         
992         ps.win_x = ps.ibufx;
993         ps.win_y = ps.ibufy;
994
995         if (maxwinx % ibuf->x) maxwinx = ibuf->x * (1 + (maxwinx / ibuf->x));
996         if (maxwiny % ibuf->y) maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y));
997
998         
999         glClearColor(0.1, 0.1, 0.1, 0.0);
1000         glClear(GL_COLOR_BUFFER_BIT);
1001
1002         GHOST_SwapWindowBuffers(g_WS.ghost_window);
1003
1004         if (sfra == -1 || efra == -1) {
1005                 /* one of the frames was invalid, just use all images */
1006                 sfra = 1;
1007                 efra = MAXFRAME;
1008         }
1009
1010         build_pict_list(&ps, filepath, (efra - sfra) + 1, ps.fstep, ps.fontid);
1011
1012         for (i = 2; i < argc; i++) {
1013                 BLI_strncpy(filepath, argv[i], sizeof(filepath));
1014                 build_pict_list(&ps, filepath, (efra - sfra) + 1, ps.fstep, ps.fontid);
1015         }
1016
1017         IMB_freeImBuf(ibuf);
1018         ibuf = NULL;
1019
1020         pupdate_time();
1021         ptottime = 0;
1022
1023         /* newly added in 2.6x, without this images never get freed */
1024 #define USE_IMB_CACHE
1025
1026         while (ps.go) {
1027                 if (ps.pingpong)
1028                         ps.direction = -ps.direction;
1029
1030                 if (ps.direction == 1) {
1031                         ps.picture = picsbase.first;
1032                 }
1033                 else {
1034                         ps.picture = picsbase.last;
1035                 }
1036
1037                 if (ps.picture == NULL) {
1038                         printf("couldn't find pictures\n");
1039                         ps.go = FALSE;
1040                 }
1041                 if (ps.pingpong) {
1042                         if (ps.direction == 1) {
1043                                 ps.picture = ps.picture->next;
1044                         }
1045                         else {
1046                                 ps.picture = ps.picture->prev;
1047                         }
1048                 }
1049                 if (ptottime > 0.0) ptottime = 0.0;
1050
1051                 while (ps.picture) {
1052                         int hasevent;
1053 #ifndef USE_IMB_CACHE
1054                         if (ibuf != NULL && ibuf->ftype == 0) IMB_freeImBuf(ibuf);
1055 #endif
1056                         if (ps.picture->ibuf) {
1057                                 ibuf = ps.picture->ibuf;
1058                         }
1059                         else if (ps.picture->anim) {
1060                                 ibuf = IMB_anim_absolute(ps.picture->anim, ps.picture->frame, IMB_TC_NONE, IMB_PROXY_NONE);
1061                         }
1062                         else if (ps.picture->mem) {
1063                                 /* use correct colorspace here */
1064                                 ibuf = IMB_ibImageFromMemory((unsigned char *) ps.picture->mem, ps.picture->size,
1065                                                              ps.picture->IB_flags, NULL, ps.picture->name);
1066                         }
1067                         else {
1068                                 /* use correct colorspace here */
1069                                 ibuf = IMB_loadiffname(ps.picture->name, ps.picture->IB_flags, NULL);
1070                         }
1071
1072                         if (ibuf) {
1073
1074 #ifdef USE_IMB_CACHE
1075                                 ps.picture->ibuf = ibuf;
1076 #endif
1077
1078                                 BLI_strncpy(ibuf->name, ps.picture->name, sizeof(ibuf->name));
1079
1080                                 /* why only windows? (from 2.4x) - campbell */
1081 #ifdef _WIN32
1082                                 GHOST_SetTitle(g_WS.ghost_window, ps.picture->name);
1083 #endif
1084
1085                                 while (pupdate_time()) PIL_sleep_ms(1);
1086                                 ptottime -= swaptime;
1087                                 playanim_toscreen(&ps, ps.picture, ibuf, ps.fontid, ps.fstep);
1088                         } /* else delete */
1089                         else {
1090                                 printf("error: can't play this image type\n");
1091                                 exit(0);
1092                         }
1093
1094                         if (ps.once) {
1095                                 if (ps.picture->next == NULL) {
1096                                         ps.wait2 = TRUE;
1097                                 }
1098                                 else if (ps.picture->prev == NULL) {
1099                                         ps.wait2 = TRUE;
1100                                 }
1101                         }
1102
1103                         ps.next_frame = ps.direction;
1104
1105
1106                         while ( (hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0)) || ps.wait2 != 0) {
1107                                 if (hasevent) {
1108                                         GHOST_DispatchEvents(g_WS.ghost_system);
1109                                 }
1110                                 /* Note, this still draws for mousemoves on pause */
1111                                 if (ps.wait2) {
1112                                         if (hasevent) {
1113                                                 if (ibuf) {
1114                                                         while (pupdate_time()) PIL_sleep_ms(1);
1115                                                         ptottime -= swaptime;
1116                                                         playanim_toscreen(&ps, ps.picture, ibuf, ps.fontid, ps.fstep);
1117                                                 }
1118                                         }
1119                                 }
1120                                 if (!ps.go) {
1121                                         break;
1122                                 }
1123                         }
1124
1125                         ps.wait2 = ps.sstep;
1126
1127                         if (ps.wait2 == 0 && ps.stopped == 0) {
1128                                 ps.stopped = TRUE;
1129                         }
1130
1131                         pupdate_time();
1132
1133                         if (ps.picture && ps.next_frame) {
1134                                 /* always at least set one step */
1135                                 while (ps.picture) {
1136                                         ps.picture = playanim_step(ps.picture, ps.next_frame);
1137
1138                                         if (ps.once && ps.picture != NULL) {
1139                                                 if (ps.picture->next == NULL) {
1140                                                         ps.wait2 = TRUE;
1141                                                 }
1142                                                 else if (ps.picture->prev == NULL) {
1143                                                         ps.wait2 = TRUE;
1144                                                 }
1145                                         }
1146
1147                                         if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) break;
1148                                         ptottime -= swaptime;
1149                                 }
1150                                 if (ps.picture == NULL && ps.sstep) {
1151                                         ps.picture = playanim_step(ps.picture, ps.next_frame);
1152                                 }
1153                         }
1154                         if (ps.go == FALSE) {
1155                                 break;
1156                         }
1157                 }
1158         }
1159         ps.picture = picsbase.first;
1160         anim = NULL;
1161         while (ps.picture) {
1162                 if (ps.picture && ps.picture->anim && (anim != ps.picture->anim)) {
1163                         // to prevent divx crashes
1164                         anim = ps.picture->anim;
1165                         IMB_close_anim(anim);
1166                 }
1167
1168                 if (ps.picture->ibuf) {
1169                         IMB_freeImBuf(ps.picture->ibuf);
1170                 }
1171                 if (ps.picture->mem) {
1172                         MEM_freeN(ps.picture->mem);
1173                 }
1174
1175                 ps.picture = ps.picture->next;
1176         }
1177
1178         /* cleanup */
1179 #ifndef USE_IMB_CACHE
1180         if (ibuf) IMB_freeImBuf(ibuf);
1181 #endif
1182
1183         BLI_freelistN(&picsbase);
1184 #if 0 // XXX25
1185         free_blender();
1186 #else
1187         /* we still miss freeing a lot!,
1188          * but many areas could skip initialization too for anim play */
1189         
1190         BLF_exit();
1191 #endif
1192         GHOST_DisposeWindow(g_WS.ghost_system, g_WS.ghost_window);
1193
1194         /* early exit, IMB and BKE should be exited only in end */
1195         if (ps.dropped_file[0]) {
1196                 BLI_strncpy(filepath, ps.dropped_file, sizeof(filepath));
1197                 return filepath;
1198         }
1199         
1200         IMB_exit();
1201         BKE_images_exit();
1202         DAG_threaded_exit();
1203
1204         totblock = MEM_get_memory_blocks_in_use();
1205         if (totblock != 0) {
1206                 /* prints many bAKey, bArgument's which are tricky to fix */
1207 #if 0
1208                 printf("Error Totblock: %d\n", totblock);
1209                 MEM_printmemlist();
1210 #endif
1211         }
1212         
1213         return NULL;
1214 }
1215
1216
1217 void WM_main_playanim(int argc, const char **argv)
1218 {
1219         bool looping = true;
1220
1221         while (looping) {
1222                 char *filepath = wm_main_playanim_intern(argc, argv);
1223
1224                 if (filepath) { /* use simple args */
1225                         argv[1] = "-a";
1226                         argv[2] = filepath;
1227                         argc = 3;
1228                 }
1229                 else {
1230                         looping = false;
1231                 }
1232         }
1233 }