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